Let’s talk about the Java generic types. Most of the time, we add generics to make lists type-safe.
We have pre-java 5 lists without generics that can get all known object’s types and when you get an object out of it we don’t know its type. When we use generics we already know what goes in and what goes out.
// a pre-Java 5 list! Don't use it!
List myList = new ArrayList();
myList.add("Fred");
myList.add(new Integer(42));
String s = (String) myList.get(1); // runtime error!
// a Java 5 and later list! Use it!
List<Dog> aList = new ArrayList<>();
aList.add(aDog);
Dog d = aList.get(0);
We can mix old non-generic code with new generic code. In this case the compiler generates a warning (when we add something to that collection). Generics are strictly a compile-time protection.
The bad moment for non-generic collections is only when you try to get all sort of objects from it. There isn’t a type-safe protection at runtime.
Polymorphism and Generics
Generics don’t work with polymorphism. You can’t assign a child generic to a parent generic.
List<Object> myList = new ArrayList<Button>(); // not compiles!
We can only assign a generic to a generic:
List<Object> myList = new ArrayList<Object>();
This isn’t right for arrays and also simple objects.
Object[] myArray = new Button[3]; // OK
The reason is that there is a JVM exception for arrays (ArrayStoreException) and the type of generics is removed at runtime.
It’s bad but legal to add a child type to a collection.
public void addAnimal(List<Animal> animals) {
animals.add(new Dog()); // legal
}
Also if you try to add a child not compiles:
List<Dog> animals = new ArrayList<Dog>();
addAnimal(animals); // not compiles!
Wildcards
The only way to pass a subtype is with ? wildcard. An example:
void foo(List<? extends Animal> list) { ... }
With this wildcard we can say we’ll pass only a type or subtype of Animal.
We can also use <? super Dog>
to say that we can pass a type and its supertypes. With that case we’ll have lists of Dogs, Animals or Objects. If we use <?>
we can pass all objects. It’s different from <Object>
because with that we can only pass lists of Objects (not subtypes).
void foo(List<?> list) { ... }
is different from:
void foo(List<Object> list) { ... }
Pay attenction that we can’t have ?
with new
keyword:
List<? super Animal> dList = new ArrayList<Dog>(); // not compiles!
List<?> list = new ArrayList<Dog>(); // OK
List<? extends Dog> bList = new ArrayList<Animal>(); // OK
We can’t add things to a collection with a wildcard because the compiler doesn’t know if we’ll add bad things.
void doInsert(List<?> list) {
list.add(new Dog()); // not compiles!
}
Java generic types declarations
When we put/add a type to a list we must know that it use and declare generics:
public interface List<E>
E stands for Elements, used with collections.
In it you can find generic methods like:
boolean add(E o)
T stands for Type, used in simple objects. An example:
public class RentalGeneric<T> {
private List<T> rentalPool;
public RentalGeneric(List<T> rentalPool) {
this.rentalPool = rentalPool;
}
public T getRental() {
return rentalPool.get(0);
}
public void setRental(T rental) {
rentalPool.add(rental);
}
}
If you try to insert something different you’ll have a compiler error.
We can create also more than one generic type in a single class definition:
public class UseTwoGenerics<T, U> {
T one;
U two;
T getT() { return one; }
U getU() { return two; }
}
We can also use a wildcard notation to specify a range:
public class AnimalGen<T extends Animal> {
T animal;
}
We can also create generic methods. Pay attenction because we have to add the type before the return argument.
public <T> void makeArrayList(T t) {
List<T> list = new ArrayList<T>();
list.add(t);
}
We can also add boundaries: <T extends Number>
And we can also create a generic constructor:
public <T> Radio(T t) {}
Note that it hasn’t a return type but a generic type.
Pay attenction to when to use a wildcard and when to use a generic type declaration.
We can’t use wildcards in class or method declarations but only in references and arguments.
public class NumberHolder<? extends Number> {} // not compiles!
public class NumberHolder<?> { ? aNum; } // not compiles!
List<?> list = new ArrayList<Dog>(); // OK, it's a reference.
void foo(List<?> list) {} // OK, it's an argument.
That’s all for the Java generic types.
Try them at home!