Reputation: 4991
I have a simple interface like this.
public interface EntityClass
{
public void setId(final Integer id);
public Integer getId();
}
and a class
public Student implements EntityClass{}
But this code is giving me error i want to know the Generic Type
public class MyGenericType<T extends EntityClass>
{
private final Class<? extends EntityClass>genericClazz;
public MyGenericType()
{
genericClazz=(Class<? extends EntityClass>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return;
}
}
Later i try this code like this
public static void main(String[] args)
{
final MyGenericType<Student>action = new MyGenericType<>();
}
But it throws
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
This code i been using it but extending from subclass but i dont want to my GenericType
extending from any class.
What can i do to be able to get the Student
type from this code.
final MyGenericType<Student>action = new MyGenericType<>();
Because i need to get a Hibernate namedQuery.
Upvotes: 2
Views: 962
Reputation: 34460
You can't get information about the generic type because of erasure, which, in short, means that the Java compiler doesn't keep generic type information about the actual instance in the generated byte code.
However, the compiler does not remove generic type information of the superclass of the actual instance. So you can use (ParameterizedType) this.getClass().getGenericSuperclass()
on a descendant of the class that has a generic type parameter, and, for that, you need to use the extends
keyword:
public interface EntityClass {
public void setId(final Integer id);
public Integer getId();
}
public class Student
implements EntityClass {
// getters and setters
}
public abstract class MyGenericType<T extends EntityClass> {
private final Class<T> genericClazz;
@SuppressWarnings("unchecked")
public MyGenericType() {
this.genericClazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
protected Class<T> getGenericClazz() {
return this.genericClazz;
}
}
public class MyStudentType // no way to avoid extending a generic type
extends MyGenericType<Student> {
@Override
public String toString() {
return this.getGenericClazz().getSimpleName();
}
}
public class Sample {
public static void main(String[] args) {
final MyStudentType action = new MyStudentType();
System.out.println(action); // Student
}
}
One comment regarding this cast within MyGenericType<T>
class' constructor:
genericClazz = (Class<? extends EntityClass>)...
Since T
is declared to extend from EntityClass
, you don't need to use wildcards. Using just T
would suffice:
genericClazz = (Class<T>)...
Regarding the ClassCastException
you're getting, that's because in your example, MyGenericType
doesn't have a generic superclass (actually, it doesn't have any superclass, except for Object
, which is not a ParameterizedType
. So, when you use this cast: (ParameterizedType) getClass().getGenericSuperclass()
, you're getting a ClassCastException
.
EDIT:
I forgot you were asking how to do this without having to extend from any class. One way is to pass the generic type in the constructor:
public class MyGenericType<T extends EntityClass> {
private final Class<T> genericClazz;
public MyGenericType(Class<T> clazz) {
this.genericClazz = clazz;
}
@Override
public String toString() {
return this.genericClazz.getSimpleName();
}
}
And then:
public class Sample {
public static void main(String[] args) {
final MyGenericType<Student> action = new MyGenericType<>(Student.class);
System.out.println(action); // Student
}
}
EDIT 2:
Another way is to make your MyGenericType
class abstract
and let subclasses return the generic type. This might be enforced by generics, so that if a subclass attempts to return the wrong type, it gets a compilation error:
public abstract class MyGenericType<T extends EntityClass> {
protected abstract Class<T> getGenericType();
@Override
public String toString() {
return this.getGenericType().getSimpleName();
}
}
public class MyStudentType
extends MyGenericType<Student> {
@Override
protected Class<Student> getGenericType() {
return Student.class;
}
}
And then:
public class Sample {
public static void main(String[] args) {
final MyStudentType action = new MyStudentType();
System.out.println(action); // Student
}
}
Upvotes: 1
Reputation: 1406
1) There is no such thing as not inheriting from any class, every thing is an extension of the Object class, so even if you don't specify anything, by default it will extend Object.
2) There are 2 ways to get the runtime type in the generic class, you can use getClass on a variable, or pass in the class in the constructor or method. Sample code:
Method 1 (Preferred if possible):
public class MyGenericType<T extends EntityClass>
{
public <T extends EntityClass> void someMethod(final T someVar)
{
Class c = someVar.getClass();
}
}
Method 2 (if you need to determine at construction time, this is the only way):
public class MyGenericType<T extends EntityClass>
{
private final Class<? extends EntityClass>genericClazz;
public MyGenericType(Class t)
{
genericClazz = t;
return;
}
}
Upvotes: 1