Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Java Generics cheat sheet

This post continues our series of one-page printable cheat sheets about Java and related technologies that we’ve been producing for almost a year now.
Today it’s all about Java generics. The feature was added to Java 10 years ago, and even today it still confuses many Java developers.

java-generics-cheat-sheet-v5

You’re welcome to download and print this cheat sheet. It fits perfectly onto a sheet of A4 paper, so if you know of someone who mixes up their generics, please share it with them.

Other installments of the cheat sheets series include:

the most used Git commands
– an introduction to SQL
Java 8 best practices cheat sheet
– a guide to Spring Framework annotations
JUnit annotations cheat sheet

and a few more.

Let’s get back to Java generics. In this post, I’ll explain the brief information we put on the cheat sheet with more context.

Download Java Generics CHEAT SHEET!

Java Generics

Generics were added to the now ancient Java 5 way back in 2005 when the world was a much simpler place. The main idea of generics was to enhance the Java compiler by using additional information on the classes for additional type safety. The most prominent user of generics is, perhaps, the Java Collections framework, which consists of the classes that act as containers for other objects. Generics, as mentioned in the official tutorial by Oracle, allow developers to implement a collection of types in a single declaration, reducing the verbosity of your code as well as its cost of maintenance.

Using Java Generics, a class can be parameterized with a type argument. For instance, consider class A, in the example below.

    class A {
    ...
    }

You can make it a generic type by declaring it as A (pronounced A of T). Now, you can use the type variable T, in the body of class A, as if it were a proper type.

    class A<T> { 
    T myFieldOfTypeT;
    ...
    }

To instantiate A, you need to provide the arguments for the type parameters, which is the actual type you want to use:

    A<Long> a = new A<Long>(); 

Now a is an instance of the A class, which is a proper parameterized generic class. Here we need to clarify a few definitions:

  • A generic type is a class that is parameterized with type arguments, for example, ArrayList<T>.
  • A parameterized type is a class where the type parameter is instantiated with a fixed argument, for example, ArrayList<Long>.
  • A raw type is a generic type that is not parameterized with anything, like new ArrayList().

But, why would you use a raw class? Well, in a nutshell, there are no benefits of using raw classes whatsoever.

If you’re to take away one thing about the generics, here’s the most important bit:
Generics do not exist at runtime! Generics are compile-time only. It means they are a utility for you to have a more readable code.

Wildcards

You will often find a ‘?’ Symbol inside a generic parameter. It is a wildcard and stands for an arbitrary type. For example, an ArrayList<?> is an array list of any type. It means that it can represent an ArrayList of Strings, or an ArrayList of Integers, or whatever type. However, at the runtime, it will have some fixed type.

You might wonder what’s the difference between declaring your variable as List<Object> versus List<?>? In a nutshell, Collection<Object> is heterogeneous collection. It is parameterized with the common superclass of all classes in Java: java.util.Object. It means that this collection can take any object, and if you get an object from it, you cannot expect it to be anything more specific than an instance of the Object class.

On the other hand, Collection<?> is a homogenous collection of arbitrary type. It means that at some point of time the compiler will figure out the bound for the types itself. So if the situation arises, we’ll get a Collection<Set> or Collection<CharSequence>, but it will be parameterized specifically. So it’s a very generic way of writing code.

In general, you can safely avoid wildcards in the generics, until you understand what it is doing to your code. Most of the time you’ll be completely fine without them.

Method overloading and overriding

When you use generics with overloaded methods, you may be surprised at what you see. Imagine the code below. What do you think the return value will be if you call generic(“hello world”)?

    String f(Object s) { 
      return "object";
    } 
    String f(String s) { 
      return "string";
    } 
    <T> void generic(T t) { 
      f(t); 
    } 

It can appear unreasonable, but the returned value will be “object.” The reason for that is that generics do not exist at runtime. It’s a compile time concept, and the compiler will generate just a single implementation of the generic method. And since the code has to be ready to accept Object as a parameter, it will only generate code that calls f(Object s).

The situation with overriding methods is often even trickier. So be prepared to dive into the implementation details and peek at the generated bytecode to better understand the behavior.

Producer Extends Consumer Super (PECS)

Take a look at the centerpiece of the cheat sheet, the PECS image. PECS stands for Producer Extends Consumer Super, and it’s a nice rule of thumb for designing an API that uses generics.

java-generics-cheat-sheet-graphic-v1

The image is a reworked illustration by Andrey Tyukin available under the CC-BY-SA.

First of all, there are two keywords used for type parameters. The extends keyword, in the <T extends X> declaration means that T is restricted to the subtypes of X, including X itself. The super means the opposite: <T super X> restricts T to be X or a superclass of X.

For the explanation of the PECS concept, look at the signature of the Collections.sort method.

Collections.copy(List<? super T> dest, List<? extends T> src)

The copy methods copies (duh!) elements from the src list to the dest list. The src list produces elements of type T or subtypes of T. The dest list accept elements, of type T or supertypes of T.

When you’re designing your own API, think about how you want to use generic collections in your code. If you’re retrieving elements from it, parameterize your method arguments using <? extends T>. It will make your code more inclusive, as the method will be usable not just by collections of T, but by its subtypes too. On the other hand, if you intend to put the elements into the collection, parameterize it with <? super T>, so someone can pass you any collection that accepts T: a collection of T, or a collection of Objects, for example.

If you plan on using the collection for both types of operations: consuming objects and producing them, it becomes harder. You probably need to just parameterize it as <T>, but then again maybe you want to rethink your API altogether. If you have a hierarchy of types, the collections of these types follow the hierarchy when they are producers, and the reverse when the hierarchy are consumers.

In the cheat sheet, we also mention recursive generics declarations. You can use this neat trick to add additional constraints on your type parameters to allow the compiler to infer more information about the types in your code. For a more detailed explanation of the concept and how to use it, watch the excellent Virtual JUG session on generics by Richard Warburton and Raoul-Gabriel Urma.

Before we go, let’s reiterate the most important point you have to keep in mind about the generics: Java generics do not exist at runtime, it’s a compile-time concept. The main idea is to allow you write more general, less verbose code that is easier to read and maintain.

Conclusion

In this cheat sheet, we looked at Java generics, including their use cases, rules of thumb, and best practices. We by no means we think that a one-page cheat sheet with a blog accompanying it can explain all of the details you need to master generics, but it’s a nice starting point.

Perhaps the most complete and advanced resource about Java Generics is the FAQ by Angelika Langer. It’s not a tutorial, but it answers almost all the questions you might have about Java generics. Or you can go through the Oracle tutorial about Java Generics.

Both are quite long, so if you prefer video Richard Warburton and Raoul-Gabriel Urma have a great session about Java Generics, their past, present and future development.

While you’re watching that, take a look at the cheat sheet one more time, we’ve really tried to make it pleasant to look at!

SHOW ME A PRINTABLE Java Generics CHEAT SHEET!


Read next: