a crowd of people release paper lanterns with candles inside that make them float into the sky. the dark sky is full of small glowing lanterns floating away.
Unrelated image from pexels.com to make this post look nicer in social media shares.

Every so often you’ve got to go back to basics no matter how long you’ve been doing something. It’s amazing how much you forget when you haven’t done something in a while, even if it’s kind of a core feature of a language you use all the time. Like Generics in Java!

So let’s talk about Generics. First of all, what is a Generic? It’s just a placeholder for a type. Where you would normally put a concrete type like String or Integer, you can put T instead.

Why should we care, though?

In short, because class cast exceptions are a pain to deal with. Back in the dark time before Java 5, you could put any object you wanted in a Collection but for that to work with Java’s type system, all of the Collections classes had to work with Objects. That meant you could put anything in, but when you got it out all Java knew was that it was an Object, so you had to know what to cast it to to use it for anything interesting.

As a bit of an aside: yes you can use arrays (not to be confused with ArrayLists) to avoid dealing with casting, but then you miss out on all the handy stuff Collections do for you, like automatically resizing themselves when you add more items. Collections for the win!

Compared to having to resize arrays manually, having to cast your objects back to the object you really wanted when you take them out of a Collection doesn’t sound so bad, but here’s the big problem with that: what if you mess up somewhere along the line and try to cast that object to something it can’t be cast to? Then you get a ClassCastException, which is really irritating because it’s a runtime exception (I should write a post about exception handling in Java shouldn’t I) for something the compiler shouldn’t have let you do in the first place. Not finding out your code is wrong in a totally predictable way until you run it sucks.

Generics to the rescue! With Generics, you can tell a collection what kind of things go in it when you create it, and then not have to do any casts when you take them out because you can only put one (depending on how you count subclasses) type of thing in there.

Okay great, but how do you actually use Generics?

If you just want to put things in a Collection and get them back out, it’s really simple. In general using existing code that uses Generics is super easy.

List<String> names = new ArrayList<>();
names.add("Amy");
names.add("Brianna");
names.add("Cara");

String name = names.get(0);

While I’m at it the <> operator (aka the diamond operator) is great. Before Java 7 you had to specify the whole type in both the declaration and instantiation, which kinda sucked if you needed, say a Map<String, List<ReallyLongTypeHere>>.

Where things get a little more complicated is writing your own code using Generics. There are two places you can put generic types, on the class declaration and on the method declaration and the really fun part is you can put different types in each place. You could put a different generic type on each method if you wanted to, but that would probably be evil so don’t do it :)

Let’s look at the List source code for an example:

public interface List<E> extends Collection<E> {
    int size();
    boolean isEmpty();
    boolean add(E e);
    E get(int index);
    <T> T[] toArray(T[] a);
}

FYI that’s a tiny subset of all the methods on the List interface, I just didn’t want to list a ton of methods that aren’t relevant to this post.

When you put a generic type like E on the class (or interface!) declaration, what you’re saying is that this class primarily deals with Es. That way when you use the same generic in methods like add and get, it’s obvious what’s going on.

Why E instead of any other letter? It’s short for element. This post has a nice list of the naming conventions for generic types. I can’t link directly to that section, so just search for “naming convention” and you’ll find it.

The toArray method declaration shows how you can use another generic type just for one method even if the class already has a generic type. The <T> just means that method takes a generic type T, it’s separate from the return value. Basically, every time you use a generic type, you’ve got to have a <T> (or <E>, or <N>, etc) somewhere so Java knows you’re using a generic and doesn’t go looking for a class named T.

One thing that’s a little tricky with generic types is getting their Class object. You need a Class for things like using Jackson to convert Json into an object in your system, but you can’t do E.class. Luckily, there’s a way around that, you can use Class<E> like so:

//here's an example method using a generic Class
public static <T> T decode(String json, Class<T> destinationClass) { //code goes here }

//and here's how you call it
Result decoded = decodeUtil.decode(myString, Result.class);

I forgot about Class<T> once and made a real mess of my code, but at least you get to learn from my mistakes. A good rule of thumb for using generics is that if you still need to cast anything, something is wrong.

There are a bunch more fancy things you can do with bounded generic types and wildcards, but I’ll get to those in another post. What I’ve covered in this post is the majority of what you’re likely to need to do with Generics, you’ll need to get a handle on this stuff anyway before the advanced stuff makes sense.