Generics: adding an extra layer of abstraction over types.
Generic Methods
These are some properties of generic methods:
- Generic methods have a type parameter (the diamond operator enclosing the type) before the return type of the method declaration.
- Type parameters can be bounded (we explain bounds later in this article).
- Generic methods can have different type parameters separated by commas in the method signature.
- Method body for a generic method is just like a normal method.
Here’s an example of defining a generic method to convert an array to a list:
public <T> List<T> fromArrayToList(T[] a) {
return Arrays.stream(a).collect(Collectors.toList());
}As mentioned, the method can deal with more than one generic type. Where this is the case, we must add all generic types to the method signature.
Here is how we would modify the above method to deal with type T and type G:
public static <T, G> List<G> fromArrayToList(T[] a, Function<T, G> mapperFunction) {
return Arrays.stream(a)
.map(mapperFunction)
.collect(Collectors.toList());
}Remember that type parameters can be bounded. Bounded means “restricted,” and we can restrict the types that a method accepts.
For example, we can specify that a method accepts a type and all its subclasses (upper bound) or a type and all its superclasses (lower bound).
To declare an upper-bounded type, we use the keyword extends after the type, followed by the upper bound that we want to use:
public <T extends Number> List<T> fromArrayToList(T[] a) {
...
}Using Wildcards With Generics
Wildcards are represented by the question mark ? in Java, and we use them to refer to an unknown type. Wildcards are particularly useful with generics and can be used as a parameter type.
Type Erasure
Generics were added to Java to ensure type safety. And to ensure that generics won’t cause overhead at runtime, the compiler applies a process called type erasure on generics at compile time.
Generics and Primitive Data Types
One restriction of generics in Java is that the type parameter cannot be a primitive type. To understand why primitive data types don’t work, let’s remember that generics are a compile-time feature, meaning the type parameter is erased and all generic types are implemented as type Object.