Generics in Java explained

What are generics, anyway?

Vasco Veloso
Javarevisited
3 min readMay 1, 2021

--

Recently, I had to explain what generics are and how they are used in the Java language. In a nutshell. Within a couple of minutes.

I ended up stating that generics are a mechanism that allows us to write code that does not care about the type of objects it is handling while at the same time giving enough information to the compiler to preserve type safety. Kind of a note left behind for the compiler.

Photo by Kelly Sikkema on Unsplash

We can not use Object to represent any type all the time

Let us get started with the classic example of a list. Forgetting for a moment that List and LinkedList are generic types, the following is legal Java and creates a list holding any object type.

List myList = new LinkedList();

That means that this is legal:

myList.add(“Hello”);

And that this is also legal:

myList.add(1);

This mixture of types feels fishy. Can we also write the following loop?

for (Integer i : myList) System.out.println(i);

No. Because the list does not carry type information about its elements, the compiler can not verify whether each assignment to the variable i is valid. Instead, we must write:

for (Object i : myList) System.out.println(i);

If we intend to handle each list element as an integer to perform some arithmetic operation, it gets ugly:

for (Object i : myList) System.out.println(((Integer) i) * 2);

In fact, it got so ugly that any element that is not an integer will now crash the application with a ClassCastException. This is the problem when using Object to represent some unknown type: the code becomes brittle when an assumption is made about what that type represents.

Fixing the problem by using type parameters

When we take advantage of the generic type and write the following instead, that problem is solved because the compiler can now do all checks for us:

List<Integer> myList = new LinkedList<Integer>()myList.add(1);myList.add(“Hello”); // compiler error: now it is illegal.for (Integer i : myList) System.out.println(i * 2); // beautiful.

Now, we can be sure that the list contains nothing else but integers.

A type parameter is, first and foremost, an instruction for the compiler. Fellow humans come second.

interface SomeCollection<T> {  void add(T element);}

When we declare an interface such as the above, we’re telling the compiler that it must ensure that the argument to the add() method will always be of the type specified by the user code (T). Whatever type might it be, we do not care, as long as it is always the same!

Generic types can also be used at the method level. Let’s say we declare this function in some class:

public <T,R> Optional<R> process(T input, Predicate<T> validator, Function<T,R> processor) {  return validator.test(input)    ? Optional.ofNullable(processor.apply(input));    : Optional.empty();}

This function is a little bit convoluted, but it is an appropriate example nevertheless. Here, we use generic types to tell the compiler two things:

  1. That the validator and the processor must handle compatible types, represented by T.
  2. That our function returns an instance of Optional wrapping the processor’s return value, represented by R.

Again, we do not care which types those are. We only want to ensure that they are compatible.

When the generic type must have some standard semantics

It is possible to place restrictions on the generic type declarations using the extends keyword to expose a known interface. This way, the generic type becomes meaningful and useable within the method or class. However, the underlying concept remains. Any type whatsoever can be used in place of the generic type, as long as it implements the interface specified.

class MyWrapper<T extends MyOtherInterface> {  // use T in some manner}

Conclusion

Generic types in Java allow us to write high-level code that can handle different object types without knowing which exact types are those while maintaining type safety.

--

--

Vasco Veloso
Javarevisited

I'm a polyglot senior software developer, currently interested in software design and architecture.