Java generics help when you want to reuse code. Suppose you write a method that prints out the output of an object using the method toString()
class Fish {} public class PrintMe { void printOut(Fish fish){ System.out.println(fish.toString()); } }
If instead of a Fish
we wanted to print a Bird
we'd be forced to overload printOut
to accept a Bird
. An option would be to write
void printOut(Object anObject){ System.out.println(anObject.toString()); }
However, one is forced to do a lot of castings because this method really accepts only Objects
not any object that inherits from it
printOut((Object) fish)
Generics turn helpful in this occasion:
public class PrintMe<T>{ void <S> printOut(S in){ System.out.println(in.toString()); } }
Here we tell Java that there are two generic types T
and S
and that we are going to use it in the class PrintMe
and in the method printOut
.
The main problems come from type erasure. In practice the compiler does the castings in the background and creates a generic class or method as if it would handle Object
s.
So, with generics you can't
instanceof
on a generic type. For Java List<String> or List<Integer> are the same!!!Object
. Use wrappers instead.
Static properties are not allowed because Java does not know until runtime the exact type. In practice it would know it only when you instantiate a generic class! In Java the compiler does this trick of casting the generic type to an Object
and therefore it looses control of it. In C++ instead, the compiler creates a number of real classes with the appropriate generic type. If Java would work as C++ static types would be allowed.
Wildcards are used in the case one wants to limit the types allowed by a generic method/class.
List<?> list = new ArrayList<String>();
Creates an instance of a List
of any kind. An ArrayList
of String>
will be ok, for instance.
Upper bound wildcards are used when you want to accept an instance of a class or any of its derivatives
List<? extends Exception> list = new ArrayList<IOException>();