Explain Codes LogoExplain Codes Logo

How do I get a class instance of generic type T?

java
reflection
generics
type-erasure
Nikita BarsukovbyNikita Barsukov·Nov 7, 2024
TLDR

Instantiating a generic type T is done by passing its Class<T> reference to the method that will create the object. Invoke newInstance() using the class reference to create an object of T.

public <T> T createInstance(Class<T> type) throws InstantiationException, IllegalAccessException { // Brace yourselves, I am creating life form out of thin air return type.newInstance(); // For newer versions: type.getDeclaredConstructor().newInstance(); }

Example:

// Rise, my creation! Foo fooInstance = createInstance(Foo.class);

Wrestling Java's type erasure

Despite Java's type erasure knocking out specific type information from generics at compile-time, you can use reflection to punch above its weight:

  1. ParameterizedType: Documents all the colourful types (e.g., List<String>).
  2. Anonymous subclass: A snazzy way to keep hold of runtime type info.
// A wild String has been spotted Foo<String> foo = new Foo<String>() {}; // Round one: Capture Actual type argument FOR THE WIN Type t = ((ParameterizedType) foo.getClass().getGenericSuperclass()).getActualTypeArguments()[0];

Getting your hands dirty: Practical usage

Harness the power of Spring's GenericTypeResolver

If you've got Spring Framework on your team, you can pass the ball to GenericTypeResolver to handle heavy-duty type resolution:

// Go, GenericTypeResolver! Type myGenericType = GenericTypeResolver.resolveTypeArgument(MyClass.class, GenericInterface.class);

When Spring isn't in season

When Spring isn't blooming, consider crafting your own non-generic subclass or utility method to suit your generic needs:

public class StringFoo extends Foo<String> { // Specialised: Because you're worth it }

Be safe out there!

  • ClassCastException: Stay safe - fend it off using try-catch blocks.
  • Reflection: Play nice with getClass().getGenericSuperclass() or face the wrath!
  • Type info storage: A neat trick for future you to thank past you.

Master the Art of Reflection

Birth of a new object

In need to breathe life into a class from its string representation? Class.forName() is your guardian angel:

public <T> T createInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException { // What's in a name? Apparently, a lot! Class<T> clazz = (Class<T>) Class.forName(className); return clazz.getDeclaredConstructor().newInstance(); }

Playing the field: Other tactics and considerations

Class literal shenanigans

Pepper your APIs with Class literals to flavor your generics:

public class Container<T> { private final Class<T> typeClass; public Container(Class<T> typeClass) { this.typeClass = typeClass; } public T createNew() throws Exception { // Hey world, meet my baby. return typeClass.newInstance(); } } Container<Foo> container = new Container<>(Foo.class); Foo instance = container.createNew();

Array of Everything

Jump the hoop and create a generic array, with all its type T goodies:

@SuppressWarnings("unchecked") public <T> T[] createArray(Class<T> type, int size) { // I've got 99 problems but an array ain't one return (T[]) Array.newInstance(type, size); }

Cementing roots: Abstract classes

Employ abstract classes for Class of T and bake in abstract methods:

public abstract class Instantiable<T> { protected final Class<T> type; protected Instantiable(Class<T> type) { this.type = type; } public T create() throws Exception { // Stand back - I'm about to do SCIENCE! return type.newInstance(); } }

Every problem, every solution

Tailor your approach to your needs - use Class<T> or reflection based on the problem at hand.