Java Generics – Detailed Theory Notes
1. Introduction to Generics
Generics in Java are a powerful feature that enables classes, interfaces, and methods to operate
on different types of data while maintaining type safety. They were introduced to eliminate runtime
errors caused by incorrect type casting and to improve code reusability. Generics allow the
programmer to define a placeholder for data types, which is specified when the object is created.
Before generics, Java collections could store any type of object, which often led to runtime errors.
Generics ensure that only a specific type of data can be stored, and errors are detected at compile
time.
ArrayList list = new ArrayList();
[Link]("Hello");
[Link](10); // mixed types allowed
String s = (String) [Link](1); // runtime error
ArrayList<String> list = new ArrayList<>();
[Link]("Hello");
// [Link](10); // compile-time error
2. Generic Classes
A generic class is a class that can operate on any data type. The type parameter is specified using
angle brackets (). This allows the same class to be reused for different data types without rewriting
code.
class Box<T> {
T value;
void set(T value) {
[Link] = value;
}
T get() {
return value;
}
}
Here, T is a type parameter. It can be replaced with Integer, String, Double, etc.
3. Generic Methods
A generic method is a method that can operate on different types independently of the class. It
declares its own type parameter before the return type.
class Test {
static <T> void print(T data) {
[Link](data);
}
}
The compiler automatically determines the type based on the argument passed.
4. Bounded Generics
Bounded generics restrict the type parameter to a specific range. This is useful when you want to
ensure that only certain types (like numbers) are used.
class Test<T extends Number> {
T value;
}
Here, only subclasses of Number (Integer, Double, etc.) are allowed.
5. Wildcards
Wildcards are used when the exact type is unknown. The symbol '?' represents an unknown type
and is mainly used in collections.
List<?> list; // unknown type
List<? extends Number> list1; // upper bound
List<? super Integer> list2; // lower bound
Wildcards provide flexibility while maintaining type safety.
6. Type Erasure
Type erasure is the process by which generic type information is removed at runtime. Java uses
generics only at compile time to ensure type safety, but the JVM does not retain generic type
information.
List<Integer> list = new ArrayList<>();
// becomes at runtime:
List list = new ArrayList();
7. Generics with Collections
Generics are widely used in collections such as ArrayList, HashMap, and HashSet to ensure that
only a specific type of data is stored.
ArrayList<String> list = new ArrayList<>();
HashMap<Integer, String> map = new HashMap<>();
8. Advantages of Generics
Generics provide several benefits including type safety, elimination of type casting, improved code
reusability, and early error detection during compilation.
9. Limitations of Generics
Generics cannot be used with primitive data types like int or double. They also do not retain type
information at runtime due to type erasure. Additionally, generic exceptions cannot be created.
10. PECS Rule
PECS stands for Producer Extends, Consumer Super. It is used to decide whether to use extends
or super in wildcards. If a structure produces data, use extends. If it consumes data, use super.
List<? extends Number> // read only
List<? super Integer> // write allowed