How to Get a Class Instance of Generic Type T

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

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details.

A popular solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

class Foo<T> {
final Class<T> typeParameterClass;

public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}

public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}

What's the equivalent of T.class in a generic class?

This is a limitation of generics in Java. The generics only exist at compile time, i.e. at runtime the object is just a BaseConverter, and so you can't query it about its generic type. The easiest solution is usually to pass in a Class<TModel> object (as you are doing) when you call the method. You can also pass it to the constructor for BaseConverter if you don't want to have to pass it in multiple times.

Instantiate a generic class T object and return it

Generic types are a compile-time notation for ensuring type safety. They are erased at runtime.

This means T and U do not exist at runtime. Which is why you can’t instantiate them.

You can, however, pass in the constructors yourself:

public class Button<T, U> {
private final Supplier<? extends T> tConstructor;
private final Supplier<? extends U> uConstructor;

public Button(Supplier<? extends T> tConstructor,
Supplier<? extends U> uConstructor) {

this.tConstructor = tConstructor;
this.uConstructor = uConstructor;
}

public T observe() {
return tConstructor.get();
}

public U click() {
return uConstructor.get();
}
}

And you can pass those constructors as method references:

public class FirstPage {
public Button<FirstPage, SecondPage> buttonOnFirstPage =
new Button<>(FirstPage::new, SecondPage::new);
}

How to create an instance just by generic type T

The generic information will be erased at compile time, so there will be no T anymore during runtime (you loose the information). Thats why you somewhere will need a Class<> to store the information.


The most clean & simple solution form my point of view is to pass in the class to to the constructor. I know you requested it to be without any constructor argument, but I do not think this is possible.


Code Sample

public abstract class AbstractBase<T> {

private final T genericTypeObject;

protected Base(Class<T> type){
try {
genericTypeObject = type.newInstance();
} catch (InstantiationException e) {
// Handle
} catch (IllegalAccessException e) {
// Handle
}
}
}

public class Child extends Base<SomeClass> {

public Child () {
super(SomeClass.class);
}
}

Alternative Solution

Using a Supplier (thanks for the comment @Jorn Vernee):

public abstract class AbstractBase<T> {

private final T genericTypeObject;

public AbstractBase(Supplier<T> supplier) {
genericTypeObject = supplier.get();
}
}

public class Child extends AbstractBase<SomeClass> {

public Child() {
super(SomeClass::new);
}
}

Create instance of generic class parameter

In short: No it's not possible without a reference to a constructible runtime value.

The reason is that TypeScript types only exist at compile time (not at runtime) and can't be used as values. In your example, T is only a type (not a runtime value). This is the JavaScript that your TypeScript program compilation would produce:

class Bar {}

class Foo {
constructor() { }
create() {
return new T();
}
}

const bar = new Foo().create();
// ^^^^^^^^
// Exception is thrown: ReferenceError: T is not defined

Java generics - get class?

Short answer: You can't.

Long answer:

Due to the way generics is implemented in Java, the generic type T is not kept at runtime. Still, you can use a private data member:

public class Foo<T> 
{
private Class<T> type;

public Foo(Class<T> type) { this.type = type; }
}

Usage example:

Foo<Integer> test = new Foo<Integer>(Integer.class);

Get Class-object representation of generic parameter in Java

The reason why

Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];

works was because the superclass of this happens to be a class with a parameterised type as its superclass. Therefore you can get the actual type arguments of that type. The type parameters of superclasses are stored as metadata in the class file if you write them in the source file.

However, in your case, whatever is passed to the onCatch parameter is not going to have a superclass of Consumer<T>. After all, Consumer<T> is not a class! You need to use getGenericInterfaces and find the one that has the name that starts with java.util.function.Consumer.

System.out.println(
// I've assumed the Consumer interface is the first one, to keep it brief
((ParameterizedType)onCatch.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0]
);

This would work if the caller calls onCatch like this:

onCatch(new Consumer<RuntimeException>() {
@Override
public void accept(RuntimeException e) {

}
});

The anonymous class will implement Consumer<RuntimeException>, and this information will be written to the class file representing the anonymous class.

However, if you use a lambda:

onCatch((RuntimeException e) -> {});

Then only a method like this is generated in the same class as the caller:

private static void lambda$caller$0(RuntimeException e) {

}

and at runtime, invokedynamic is used to create an instance that implements Consumer, and this is the bad news: the type parameter RuntimeException is not part of the generated class for this instance, for whatever reason.

The only way you can find RuntimeException now then, is if you somehow know who the caller is, and find the lambda$caller$0 method, and look at its parameter.

That said, everything I've wrote so far is pretty much all implementation detail, and I wouldn't use any of that in production code. I would say you should just add a Class<E> parameter:

onCatch(RuntimeException.class, e -> {});

It doesn't look that different on the caller's side anyway.



Related Topics



Leave a reply



Submit