Java Generics
Generics is a powerful feature introduced in Java SE 5.0 that allows us to use type parameters when defining classes, interfaces, and methods. Using generics enables writing more flexible, safer, and clearer code.
Why Do We Need Generics?
Before generics were introduced, Java's collection classes (like ArrayList) could only store objects of type Object. This approach had two main problems:
- Type unsafe: You could add any type of object to an
ArrayList, but errors couldn't be detected at compile time. - Required explicit type casting: When retrieving elements from a collection, you had to manually cast them to the expected type, which was cumbersome and could throw
ClassCastExceptionat runtime.
Generics perfectly solved these problems by moving type checking from runtime to compile time.
Generic Classes
We can define our own generic classes. Type parameters (usually represented by single uppercase letters, such as T for Type, E for Element, K for Key, V for Value) are declared in angle brackets <> after the class name.
Generic Methods
Besides generic classes, we can also define generic methods. Generic methods have their own type parameters, which are declared before the method return type.
Bounded Type Parameters
Sometimes we want to restrict the types that can be used as type parameters. For example, a method might only accept instances of Number or its subclasses. This can be achieved through bounded type parameters.
<T extends UpperBound>:Tmust be of typeUpperBoundor its subtype.
Wildcards
The wildcard ? represents an unknown type and is commonly used in generic method parameters to increase flexibility.
-
Upper bounded wildcard (
? extends Type): Represents the parameter type isTypeor any of its subtypes. Such collections are read-only (cannot add elements exceptnull) because we cannot determine the exact type of the collection. -
Lower bounded wildcard (
? super Type): Represents the parameter type isTypeor any of its supertypes. Such collections are write-only (can add instances ofTypeand its subtypes), but reading only returnsObject. -
Unbounded wildcard (
?): Represents any type. Used when the type doesn't matter, such asList<?>.
Type Erasure
Java generics are implemented through type erasure. This means that after compilation, all generic type information is removed. Box<String> and Box<Integer> at runtime both become the raw Box class, with the type parameter T replaced by its bound (default is Object). The compiler automatically inserts type casting code where necessary to ensure type safety.