Explain Codes LogoExplain Codes Logo

Java: how do I get a class literal from a generic type?

java
reflection-api
type-erasure
generics
Nikita BarsukovbyNikita Barsukov·Feb 26, 2025
TLDR

In Java, type erasure gets rid of generic type information once your code is compiled. As a result, you cannot retrieve a Class literal directly from a generic type. So, you'll need to pass the Class object into methods. Here's a quick example:

public <T> T instantiate(Class<T> type) { try { return type.getDeclaredConstructor().newInstance(); } catch (Exception e) { // Something went wrong... Chuck Norris was probably here. throw new RuntimeException(e); } } // Usage for a String instance String str = instantiate(String.class);

Note: This method works best for when you're dealing with types known at compile-time.

Generic types are a tough nut to crack

In Java, generics behave like a Rubik's cube: they're useful, but can be tricky to solve if you don't know the tricks. During compilation, generic type parameters are erased and converted to their raw form. This is why List<Foo>.class cannot retrieve a class literal for a parameterized type.

If you try the following:

List<String> list = new ArrayList<>(); Class<? extends List> cls = list.getClass();

You'll only retrieve information about the raw List type, not List<String>. Java does this to make sure your granny's code from the 90s can still work today.

Reflection can sometimes save the day

Despite type erasure, Java's Reflection API provides utility methods to obtain runtime type information:

  • ParameterizedType can be used to dig up generic type arguments.
  • Libraries like Gson's TypeToken can capture generic type info.

For example, to get the Type of a List<Foo>:

Type type = new TypeToken<List<Foo>>(){}.getType();

But beware! TypeToken is a powerful weapon but it's supplied by the Gson library, not standard Java.

Double casting and the unchecked warning

A quirky trick to obtain a parameterized class literal is to perform a double cast:

@SuppressWarnings("unchecked") Class<List<Foo>> listFooClass = (Class<List<Foo>>)(Object)List.class;

This will give you what looks like a class literal for List<Foo>. But be warned: it could be a ticking time bomb in terms of type safety.

Cleanup with helper methods

To avoid littering your code with unchecked casts, consider creating helper methods:

@SuppressWarnings("unchecked") public static <T> Class<T> cleanCast(Object obj) { // Cast away your problems. Just kidding, they'll come back. return (Class<T>) obj.getClass(); } // Usage Class<List<Foo>> listFooClass = cleanCast(new ArrayList<Foo>());

Caution: These are like double-edged swords. Using them in public API compromises type safety.

Type safety and the invisibility cloak

Solving puzzles with generics in Java can feel like playing a round of space chess: exciting but challenging. While generics improve type safety at compile-time, type erasure can seem to pull an invisibility cloak over this information at runtime.

Comprehend your generics better with libraries

Several libraries out there like Google's Gson offer tools like TypeToken. This can capture generic type information, useful for transforming them into JSON or similar tasks.

With TypeToken, you're not breaking any rules, you're just using a more formidable set of tools to comprehend your generics deeper.