Session 12 |
Generics: classes and method
Almost all data structures that we have studied so far contain <T>
(e.g. List<String>
). What does <T>
mean? Are List<String>
and List<Integer>
the same interface or not?
By convention, a single capital letter such as E or T is used to denote a formal generic type. The term generics means parameterized types. Parameterized types are important because they enable us to create classes, interfaces, and methods in which the type of data upon which they operate is specified as a parameter. For example, using generics, it is possible to create a single class that automatically works with different types of data. A class, interface, or method that operates on a parameterized type is called generic (e.g. generic class or generic method).
It is important to understand that Java has always given us the ability to create generalized classes, interfaces, and methods by operating through references of type Object
. Because Object
is the superclass of all other classes, an Object
reference can refer to any type object. With generics, all casts are automatic and implicit.
Here is an example of a class which can hold objects of different data types (excluding the primitive data types):
public class Holder<T> { private T holdObject; public T getHoldObject() { return holdObject; } public void setHoldObject(T holdObject) { this.holdObject = holdObject; } } public class Test { public static void main(String[] args) { Holder<Integer> intHoldObject = new Holder<>(); intHoldObject.setHoldObject(99); Holder<String> strHoldObject = new Holder<>(); strHoldObject.setHoldObject("hurraa!"); } }
Here, <T>
represents a formal generic type, which is replaced later in the client class with an actual concrete type (Integer
or String
). The name of the formal generic type can be anything; however, it is a good practice to use capitalised mnemonic names.
In the client class, when an object is created, the parameter type is initialised. Replacing a generic type is called a generic instantiation. Generally, we can think about it as if the variable T
is replaced in the object with the specific type of data.
Note: one class may have several parameter types (separated by comma). For example, in the Map<K, V>
interface, the first parameter sets the parameter type for the key and the second one sets the parameter type for the value. Remember, generic types must be reference types. We cannot replace a generic type with a primitive type such as int
, double
, or char
. In such cases we have to use the wrapper classes, e.g. Integer
, Double
, Character
etc.
The key benefit of generics is to enable errors to be detected at compile time rather than at runtime. A generic class or method permits us to specify allowable types of objects that the class or method can work with. If we attempt to use an incompatible object, the compiler will detect that error.
Bounded parameter types
A generic type can be specified as a subtype of another type. Such a generic type is called bounded.
public class NumberHolder<N extends Number> { private N numberHolder; public N getNumberHolder() { return numberHolder; } public void setNumberHolder(N numberHolder) { this.numberHolder = numberHolder; } }
Here, the bounded parameter type N
can only be any subclass of java.lang.Number
. The compiler allows to use NumberHolder<Integer>
, but not NumberHolder<String>
. Setting the bound is efficient because now the N
type variables of the NumberHolder
class can use any method of the class Number
(the compiler guarantees that N
is always Number
). The bounded parameter type can be both the interfaces and the classes (in both cases use the keyword extends
- even if it seems to be weird).
Generic methods
Sometimes it is not efficient to set a generic parameter type to the class. Generic can be a method, too. To declare a generic method, we place the generic type <T> immediately after the return type in the method header:
public static <T> List<T> reverse(List<T> list) { List<T> result = new ArrayList<T>(); for (int i = list.size()-1; i >= 0; i--) { result.add(list.get(i)); } return result; }
Session 12 |