Shekhar Gulati is a Java Consultant with over 5 years experience.He is currently working with Xebia India an Agile Software Development company.The postings on this site and on his blog are his own and do not necessarily represent opinion of his employer. His own blog is at http://whyjava.wordpress.com/ and you can follow him on twitter here. Shekhar has posted 25 posts at DZone. View Full User Profile

5 Important Points about Java Generics

08.31.2010
| 51493 views |
  • submit to reddit
Generics are one of the most controversial Java language features. Generics allows a type or method to operate on objects of various types while providing compile-time type safety, making Java a fully statically typed language. In this article, I am going to talk about five things that every Java developer should know about Generics.
  1. Generics are implemented using Type Erasure

  2. In Java a class or an interface can be declared to define one or more type parameters and those type parameters should be provided at the time of object construction. For example,
    List<Long> list = new ArrayList<Long>();
    list.add(Long.valueOf(1));
    list.add(Long.valueOf(2));
    In the example shown above a List is created which can only contain elements of type Long and if you try to add any other type of element to this list, it will give you compile time error. It helps detect errors at compile time and makes your code safe. Once this piece of code is compiled ,the type information is erased resulting into similar byte code as we would have got if the same piece of code was written using Java 1.4 and below. This results in binary compatibility between different versions of Java. So, a List or List<> are all represented at run-time by the same type, List.
  3. Generics does not support sub-typing

  4. Generics does not support sub-typing which means that List is not considered to be a sub-type of List, where S is a subtype of T. For example,
    List<Number> numbers = new ArrayList<Integer>(); // will not compile
    The piece of code shown above will not compile because if it compiles than type safety can't be achieved. To make this more clear, lets take the following piece of code shown below where at line 4 we are assigning a list of long to a list of numbers. This piece of code does not compile because if it could have compiled we could add a double value in a List of longs. This could have resulted in ClassCastException at runtime and type safety could not be achieved.
    List<Long> list = new ArrayList<Long>();
    list.add(Long.valueOf(1));
    list.add(Long.valueOf(2));
    List<Number> numbers = list; // this will not compile
    numbers.add(Double.valueOf(3.14));

  5. You can't create Generic Arrays

  6. You can't create generic arrays as shown below because arrays carry runtime type information about the type of elements . Arrays uses this information at runtime to check the type of the object it contains and will throw ArrayStoreException if the type does not match. But with Generics type information gets erased and the array store check will succeed in cases where it should fail.
    T[] arr = new T[10];// this code will not compile
    You can't even create Arrays of Generic classes of interfaces. For example, the code shown below does not compile.
    List<Integer>[] array = new List<Integer>[10]; // does not compile
    Arrays behave differently from the collections because arrays are covariant by default, which means that S[] is a subtype of T[] whenever S is a subtype of T, where as Generics does not support covariance. So, if the above code had compiled then the array store check would succeed in cases where it should fail. For example,
    List<Integer>[] ints = new List<Integer>[10]; // does not compile
    Object[] objs = ints;
    List<Double> doubles = new ArrayList<Double>();
    doubles.add(Double.valueOf(12.4));
    objs[0] = doubles; // this check should fail but it succeed
    If the generic arrays were allowed, then we could assign ints array to an object array because arrays are covariant. After that we could add a List of double to the obj array. We will expect that this will fail with ArrayStoreException because we are assigning List of double to an array of List of integers. But the JVM cannot detect type mismatch because the type information gets erased. Hence the array store check succeeds, although it should have failed.
  7. Use of wildcards with extends or super to increase API flexibility

  8. There are times that you need to work not only with T but also with sub types of T. For example, the addAll method in the Collection interface which adds all the elements in the specified collection to the collection on which it is called. addAll method has the signature
    boolean addAll(Collection<? extends E> c)
    This ? extends E makes sure that you can not only add collection of type E but also of subtype of E. ? is called the wildcard and ? is said to be bounded by E. So,if we create a List of Number then we can not only add List of number but we can also add List of Integer or any other subtype of Number.
    List<Number> numbers = new ArrayList<Number>();
    ArrayList<Integer> integers = new ArrayList<Integer>();
    ArrayList<Long> longs = new ArrayList<Long>();
    ArrayList<Float> floats = new ArrayList<Float>();
    numbers.addAll(integers);
    numbers.addAll(longs);
    numbers.addAll(floats);
    So far we have covered the use of extends with wildcards and we saw that API became more flexible after using extends . But where will we use super? Collections class has a method called addAll which add all the specified elements to the specified collection. It has following signature
    public static <T> boolean addAll(Collection<? super T> c, T... elements) ;
    In this method you are adding elements of type T to the collection c. super is used instead of extends because elements are added into the collection c whereas in the previous example of Collection interface addAll method elements were read from the collection . In the Effective Java book, Joshua Bloch calls this PECS. PECS stands for producer extends, consumer super.
    It proves very helpful whenever you are confused about whether you should use extends or super.
  9. Use of Multiple Bounds

  10. Multiple bounds is one of the generics features which most developer do not know. It allows a type variable or wildcard to have multiple bounds. For example, if you to define constraint such as that the type should be a Number and it should implement Comparable.
    public static <T extends Number & Comparable<? super T>> int compareNumbers(T t1, T t2){
    return t1.compareTo(t2);
    }
    It makes sure that you can only compare two numbers which implement Comparable. Multiple bounds follows the same constraints as followed by the a class i.e. T can't extend two classes ,you have to first specify the class then the interface, and T can extend any number of interfaces.
public static <T extends String & Number > int compareNumbers(T t1, T t2) // does not work..can't have two classes
public static <T extends Comparable<? super T> & Number > int compareNumbers(T t1, T t2) // does not work..
public static <T extends CharSequence & Comparable<T>> int compareNumbers(T t1, T t2)// works..multiple interfaces
Published at DZone with permission of its author, Shekhar Gulati.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Martijn Verburg replied on Tue, 2010/08/31 - 3:56am

Good post!  I always have to go back to my "Effective Java" to make sure I'm using Generics to their fullest potential

Mario Fusco replied on Tue, 2010/08/31 - 8:48am

Great post. Just one thing could have been explained a bit more in depth. Provided that you know at runtime the class of T (let's call it tClass) it is still possible to create a generics array with the following statement:

T[] arr =  (T[])java.lang.reflect.Array.newInstance(tClass, size); 

 

Craig Ringer replied on Tue, 2010/08/31 - 9:04am

On type erasure:

The use of type erasure  isn't all that interesting to a language end-user by its self, it's the side-effects that matter. You can't allocate an instance of a type parameter directly ("new T()") because the type isn't known at runtime. You can't access any static members of it, be they methods, variables, or special variables like ".class", for the same reason. This is why you so often see generic classes taking both a type parameter and a reference to the class object for that type, so it may be used reflectively. The pattern:

class Thing<T> {
  private final Class<T> tClass;
  public Thing(Class<T> tClass) {
    this.tClass = tClass;
  } 
} 

... is so common for just that reason.

 

Type-parameterized methods

Anyway,  another interesting one that many don't seem to know (even though it's in the tutorial) is that methods can have type parameters; it's not just classes that may be parameterized. Like in C++, method type parameters may be inferred under most circumstances, but may still be explicitly specified - and sometimes need to be.

 To call a type-parameterized method with an explicit type, use:

objectRef.<Type>functionName()

 I mostly find myself needing to make explicitly-parameterized method calls when using the JPA Criteria API's CriteriaBuilder, but I'm sure it comes up in other places.

Finally...

Thanks for the post. I'd failed to notice the facility for multiply-constrained type parameters, and there's a chance it might be handy down the track.

 (Fireproof underwear on) Oh, if only there were reified generics too/instead ...

Andrew McVeigh replied on Tue, 2010/08/31 - 9:46am

List<Number> numbers = new ArrayList<Integer>(); // will not compile

It's true it won't compile, as you point out, because you could potentially then insert a Number into the list.

However, you can make the list effectively readonly by using a wildcard upper-bound.

    List<? extends Number> numbers = new ArrayList<Integer>();

You won't be able to write to it, but you can certainly read from it, and sub/super-classing is preserved.

Shekhar Gulati replied on Tue, 2010/08/31 - 12:04pm in response to: Martijn Verburg

Effective Java is a great book...I also follow Joshua item tips

Robert Lauer replied on Wed, 2010/09/01 - 5:58am

Thanks, this is a nice summary.

Neon Light replied on Wed, 2010/09/01 - 9:28pm

I use generics all the time but wasn't aware of mutliple bonds.

I don't remember it being mentioned in effective Java, unless I forgot about it.

Thank you for that missing information.

Tim Boudreau replied on Fri, 2010/09/03 - 3:56pm

Having recently written a bunch of code that heavily used generics (*not* in the context of collections), the biggest problem I had was that other developers were baffled by it. I've actually been looking at reducing my use of them a bit, to ensure that if I design an interface, someone is not mystified by how to use it.

One place multiple bounds is useful is when working with Enums. For example:

public abstract class RelationshipTable<SubjectType, RelationshipType extends
        Enum<RelationshipType> & Related, ObjectType> {
defines a generic data structure for relationships between objects, where the relationships will be some kind of enum which implements an interface Related.

Another pattern that is very useful is where you have a reusable runnable-like object which ought to take an argument, and you want it to be stateless and take an argument of an unknown type:

public interface Operation <ArgType, ResultType> {
  public ResultType run (ArgType argument);
}

public class Session {
   public <ArgType, ResultType> Future<ResultType> runOperation (Operation <ArgType, ResultType> op, ArgType arg) { ... }
}
Not only is it easy to have Operation implementations which are stateless, reusable wads of logic this way, but it is trivial to write an Operation subclass which chains together multiple Operations, feeding the output of one into the argument of the next, and all of this can be done by code which knows nothing about the concrete types of the inputs and outputs.

Here is an odd pattern (similar to the slightly baffling way the JDK defines Enum<T extends Enum<T>>, which is really a trick to ensure an Enum subclass can never be parameterized on anything but itself) that can be useful in a system where you have, say, a lot of longs that refer to different kinds of object, and want to ensure that you cannot have the type of bug that occurs where you, say, mix up parameter order of a bunch of longs and end up with invalid references to things:

public interface Identifiable<T extends Identifier<? super What>, What> {
    public T getIdentifier();
}
public interface Identifier<T> {
    public long getID();
}
public class User extends Identifiable<UserIdentifier, User> { ... }
public class UserIdentifier extends Identifier<User> { ... }
basically welds together User and UserIdentifier so that you cannot possibly have a UserIdentifier which identifies something which is not a User, or vice versa. While you could do this without generics, with generics, you can have generic, reusable data structures which work like our RelationshipTable above (have SubjectType and ObjectType extend Identifiable), upon such objects, which have type-safety all the way down, yet are able to collapse any set of "relationships" down to three numbers for storage (a Set<Enum> is easily converted into a bitmask - which is exactly what EnumSet does internally).

The problem is, other developers seem to be terrified when they see class signatures like this. Which is rather sad, since generics really do let you write data structures which are more generic in the sense of can be reused against more things - resulting in less code, less testing needed, more type-safety and fewer bugs. Generics are hugely useful for much more than just collections.

Javin Paul replied on Mon, 2011/07/18 - 10:08am

I must say great post , never know these details about generic. Thank a lot for bringing these to us.

Javin
How volatile keyword works in Java with example

Lokesh Gupta replied on Thu, 2013/05/16 - 12:37am

 Its great list of things to remember while working with Generics. I am still in process of learning more n more. So far, Effective Java has been a great resource. Now I am in search of similar analysis on topic. Any hint??

http://howtodoinjava.com/2013/05/15/date-and-time-api-changes-in-java-8-lambda/

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.