Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A Java generic class is a class declaration that includes one or more type parameters enclosed in angle brackets (<>). It acts as a template, allowing developers to define classes that operate on specified types while enforcing strict compile-time type safety and eliminating the need for explicit type casting.

Syntax and Declaration

A generic class is defined by appending a type parameter section to the class name. The type parameters represent non-primitive object types that will be resolved when the class is instantiated.
public class Container<T> {
    private T item;

    public Container(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}
Standard naming conventions dictate using single, uppercase letters for type parameters:
  • T - Type
  • E - Element (used extensively by the Java Collections Framework)
  • K - Key
  • V - Value
  • N - Number

Multiple Type Parameters

A generic class can declare multiple type parameters separated by commas.
public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

Instantiation and the Diamond Operator

When instantiating a generic class, the type arguments must be reference types (classes or interfaces), not primitives. Java 7 introduced the diamond operator (<>), which allows the compiler to infer the type arguments for the constructor from the variable declaration.
// Explicit type argument in constructor (Pre-Java 7)
Container<String> stringContainer = new Container<String>("Text");

// Type inference using the diamond operator (Java 7+)
Container<Integer> intContainer = new Container<>(42);
Pair<String, Double> pricePair = new Pair<>("Item", 19.99);

Bounded Type Parameters

Type parameters can be restricted using bounds. An upper bound restricts the type parameter to a specific class or its subclasses, or an interface and its implementers. This is declared using the extends keyword.
// T must be a subclass of Number
public class NumericProcessor<T extends Number> {
    private T value;

    public double getDoubleValue() {
        // Safe to call Number methods because of the upper bound
        return value.doubleValue(); 
    }
}
Multiple bounds can be specified using the & operator. If one of the bounds is a class, it must be specified first.
public class ComplexProcessor<T extends Number & Comparable<T>> {
    // T must extend Number AND implement Comparable<T>
}

Type Erasure

Generics in Java are primarily a compile-time construct. To ensure backward compatibility with older versions of Java, the compiler applies a process called type erasure. During compilation, the Java compiler:
  1. Replaces all type parameters in generic types with their bounds, or with Object if the type parameters are unbounded.
  2. Inserts type casts where necessary to preserve type safety.
  3. Generates bridge methods to preserve polymorphism in extended generic types.
While type arguments are erased from runtime instances and execution instructions (meaning instances of Container<String> and Container<Integer> share the same raw class Container at runtime), the Java compiler preserves generic type information for class declarations, fields, and method signatures in the bytecode’s Signature attribute. This allows the generic types of these structural elements to be inspected at runtime using Reflection (e.g., Field.getGenericType()).

Technical Restrictions

Due to type erasure and the mechanics of the Java Virtual Machine (JVM), generic classes have several strict limitations:
  1. No Primitive Types: Type arguments cannot be primitives. You must use wrapper classes (e.g., Integer instead of int).
  2. No Instantiation of Type Parameters: You cannot create an instance of a type parameter (new T()) because the compiler does not know the exact type of T at runtime.
  3. No Static Fields of Type Parameter: Static members are shared across all instances of the class, regardless of the type parameter. Therefore, a static field cannot be of type T.
  4. No Generic Arrays: You cannot instantiate arrays of parameterized types (new Container<String>[10]) or arrays of type parameters (new T[10]).
  5. Restricted instanceof Checks: Because type argument information is erased from instances at runtime, you cannot use the instanceof operator with specific parameterized types (e.g., obj instanceof Container<String> results in a compilation error). You can only check against the raw type (obj instanceof Container) or the unbounded wildcard parameterized type (obj instanceof Container<?>).
Master Java with Deep Grasping Methodology!Learn More