Fundamentals of Java Generics
Generics in Java enable type parameters for classes, interfaces, and methods, allowing compile-time type safety and code reuse without runtime type casting errors.
Core Concepts
Type parameters act as placeholders for actual types specified during usage. This parameterization enables compile-time validation and elmiinates explicit type casting.
Key Applications
Generic Classes
Define classes with type parameters that determine field types and method signatures.
public class Container<ElementType> {
private ElementType storedElement;
public void store(ElementType element) {
this.storedElement = element;
}
public ElementType retrieve() {
return storedElement;
}
}
// Implementation
Container<Double> numberContainer = new Container<>();
numberContainer.store(45.67);
Double result = numberContainer.retrieve();
Generic Interfaces
Interfaces can declare type parameters for implemantation by concrete classes.
public interface KeyValuePair<KeyType, ValueType> {
KeyType obtainKey();
ValueType obtainValue();
}
public class BasicPair<KeyType, ValueType> implements KeyValuePair<KeyType, ValueType> {
private final KeyType key;
private final ValueType value;
public BasicPair(KeyType k, ValueType v) {
this.key = k;
this.value = v;
}
@Override
public KeyType obtainKey() { return key; }
@Override
public ValueType obtainValue() { return value; }
}
KeyValuePair<String, Boolean> flagPair = new BasicPair<>("enabled", true);
Generic Methods
Methods can declare independant type parameters for parameters and return types.
public class ArrayProcessor {
public static <ElementType> void displayElements(ElementType[] elements) {
for (ElementType elem : elements) {
System.out.print(elem + " ");
}
System.out.println();
}
}
Character[] letters = {'A', 'B', 'C'};
ArrayProcessor.displayElements(letters);
Generic Collections
Type-safe collections prevent invalid element insertion at compile time.
import java.util.ArrayList;
import java.util.List;
List<Integer> numberSequence = new ArrayList<>();
numberSequence.add(10);
numberSequence.add(20);
// numberSequence.add("text"); // Compilation error
for (Integer num : numberSequence) {
System.out.println(num); // No casting required
}
Wildcards and Bounds
Wildcards provide flexibility for unknown types with upper and lower bounds.
// Unbounded wildcard
List<?> unknownList = new ArrayList<String>();
// Upper bounded wildcard
List<? extends Number> numericList = new ArrayList<Float>();
Number firstElement = numericList.get(0);
// Lower bounded wildcard
List<? super Integer> integerSuperList = new ArrayList<Number>();
integerSuperList.add(42);
Benefits of Generics
- Type Safety: Compile-time detection of type violations
- Code Reusability: Single implementation for multiple types
- Eliminated Casting: Automatic type conversion
- Improved Readability: Explicit type declarations in code