Generics relates to how classes that store objects can store objects of a freely chosen type.
The choice is based on the generic type parameter in the definition of the classes, which makes it possible to choose the type(s) at the moment of the object's creation.
Using generics is done in the following manner: after the name of the class, follow it with a chosen number of type parameters.
Each of them is placed between the 'smaller than' and 'greater than' signs, like this: public class Class<TypeParameter1, TypeParameter2, ...>
.
The type parameters are usually defined with a single character.
public class Locker<T> {
private T element;
public void setValue(T element) {
this.element = element;
}
public T getValue() {
return element;
}
}
The definition public class Locker<T>
indicates that the Locker
class must be given a type parameter in its constructor.
After the constructor call is executed, all the variables stored in that object are going to be of the type that was given with the constructor.
Locker<String> string = new Locker<>();
string.setValue(":)");
System.out.println(string.getValue());
In the program above, the runtime implementation of the Locker
object named string
looks like the following.
public class Locker<String> {
private String element;
public void setValue(String element) {
this.element = element;
}
public String getValue() {
return element;
}
}
There is no maximum on the number of type parameters, it's all dependent on the implementation.
Creating generic interfaces is very similar to creating generic classes.
public interface List<T> {
void add(T value);
T get(int index);
T remove(int index);
}
There are two ways for a class to implement a generic interface.
One is to decide the type parameter in the definition of the class, and the other is to define the implementing class with a type parameter as well.
public class MovieList implements List<Movie> {
// object variables
@Override
public void add(Movie value) {
// implementation
}
@Override
public Movie get(int index) {
// implementation
}
@Override
public Movie remove(int index) {
// implementation
}
}
The alternative is to use a type parameter in the class defintion, in which case the parameter is passed along to the interface. Now this concrete implementation of the interface remains generic.
public class GeneralList<T> implements List<T> {
// object variables
@Override
public void add(T value) {
// implementation
}
@Override
public T get(int index) {
// implementation
}
@Override
public T remove(int index) {
// implementation
}
}