Generics mean parameterized types. The idea is to allow type (Integer, String, etc., and user-defined types) to be a parameter to methods, classes, and interfaces. This enables you to write a single class or method that can work with different types of data while maintaining strict type safety.
To understand the power of Generics, we must look at the "Dark Ages" of Java 1.4 and earlier.
List list = new ArrayList();list.add("Hello");String s = (String) list.get(0);
Requires manual casting. No protection against adding an Integer to the same list.
List<String> list = new ArrayList<>();list.add("Hello");String s = list.get(0);
No casting required. Compiler blocks any attempt to add non-String objects.
You can create your own classes that handle any data type by using a Type Parameter (usually denoted by a single capital letter like T, E, or K).
Sometimes you don't want to accept any type; you want to limit the types to a specific family (e.g., only Numbers). This is called a Bounded Type.
public <T extends Number> void process(T num) { ... }
This method will accept Integer, Double, or Float, but will reject String at compile time.
Wildcards represent an unknown type. They are essential when writing methods that operate on collections of different related types.
| Type | Syntax | Meaning |
|---|---|---|
| Upper Bounded | ? extends T |
Accepts T or any subclass of T (Best for Reading). |
| Lower Bounded | ? super T |
Accepts T or any superclass of T (Best for Writing). |
| Unbounded | ? |
Accepts any type. |
It is crucial to understand that Generics in Java are a Compile-time feature. To maintain backward compatibility with older Java versions, the Java compiler uses a process called Type Erasure.
Object if unbounded).List<String> and a List<Integer> are exactly the same class: ArrayList.class.Because of Type Erasure, there are things you simply cannot do with Generics:
List<int>. You must use List<Integer>.T obj = new T(); because T is erased at runtime.List<Integer>[] array = new ArrayList<Integer>[10]; is illegal.This method swaps two elements in an array of any type. This is the pinnacle of code reusability.
Q: What is the PECS principle?
A: Producer Extends, Consumer Super. If you are reading from a collection, use ? extends T. If you are adding to a collection, use ? super T.
Q: Can you explain Type Erasure in one sentence?
A: It is the process where the compiler removes all generic type information and replaces it with raw types and casts to ensure compatibility with pre-generic JVMs.
Q: Why can't we use primitives as type parameters?
A: Generics are erased to Object, and in Java, primitives (like int) do not inherit from Object. This is why we use Wrapper classes.
Generics are the reason modern Java is robust and clean. By enabling type safety at compile time, they eliminate a whole class of runtime errors. While Type Erasure imposes some limits, the ability to write reusable, type-safe logic for classes and methods is one of the most important skills a Java developer can master.
Next: Comparable vs. Comparator →