Java Generics
Why generics?
- stronger type checks at compile time rather than runtime
- eliminate type casting
- enable code reusability
Type safety & eliminate type casting
Without generics
List list = new ArrayList();
[Link]("hello");
[Link](123);
Integer num = (Integer) [Link](0); // Runtime error: ClassCastException
With generics
List<String> list = new ArrayList<>();
[Link]("hello");
[Link](123); // Compile-time error
String s = [Link](0); // No casting needed
Code reusability
1.
class Box<T> {
private T value;
public void set(T value) { [Link] = value; }
public T get() { return value; }
}
Box<Integer> intBox = new Box<>();
Box<String> strBox = new Box<>();
2.
public <T> void printArray(T[] array) {
for (T element : array) {
[Link](element);
}
}
Raw Type in Generics
class Box<T> {
T value;
void set(T value) { [Link] = value; }
T get() { return value; }
}
Box rawBox = new Box(); // raw type
[Link]("Hello"); // Allowed
[Link](123); // Also allowed! Dangerous!
Object o = [Link](); // Must cast manually
Cons:
- Lost type safety
- ClassCastException in runtime
- Defeats the purpose of generics
Type Inference in Java
Automatically determine the type of a generic or variable without it being explicitly stated.
Java uses type inference mainly in three areas:
1. Generics Method calls
2. The diamond operator
3. var keyword
public static <T> void print(T item) {
[Link](item);
}
print("Hello"); // Java infers T as String
print(123); // Java infers T as Integer
print<String>("Hello"); // no need
List<String> list = new ArrayList<>(); // Java infers that it's List<String>
List<String> list = new ArrayList<String>(); // more verbose
var list = new ArrayList<String>(); // Java infers type as ArrayList<String>, determine at compile time not dynamic
How works internally?
Generics resolved in compile time, not run time.
In runtime generics become:
class Box<Object> { // type erase
Object value;
void set(Object value) { [Link] = value; }
Object get() { return value; }
}
When we call Box<String> box = new Box();
Compiler done typecasting by String.
Type erasure
● a fundamental part of Java's generics implementation.
● Java removes all generic type information at compile time,
● making generics backward-compatible with older versions
Why?
Java was designed to work with older versions (Java 1.4 and below), so generics were added in a way that didn’t change the
runtime (JVM). Instead, the compiler enforces type safety, and then erases the types before runtime.
After Erasure, ref.
Generic Declaration After Erasure
T extends Number Number
T Object
List<String> List
Problem
Box<String> box = new Box<String>();
[Link](“Generics”);
Box anotherBox = box;
[Link](100);
[Link]([Link]());
[Link]([Link]()); // ClassCastException occur
Box<Integer> anotherBox = box; // type mismatch in compile time
Why primitive Data type not Allowed?
- do not support primitive types
- generics work only with objects due to type erasure
List<Integer> becomes List at runtime.
This works for objects (because of inheritance and polymorphism), but primitives are not objects, so they can’t be used in a
type-erased context.
All object types inherit from Object, but primitives do not, so you can't write a method that works on T extends Object and pass
in a primitive.
Ref.