jax
jax

Reputation: 38643

Get generic field type at runtime via reflection

I am having trouble accessing the runtime type of id via reflection from MyClass.

public abstract class AbstractEntity<T> {

    @Id
    private T id; //I need to access the runtime class of T

}

public class MyClass extends AbstractEntity<Long> {} 

I am using java reflection and have access to the id Field in the superclass.

I tried using:

I want to access Long as the field type.

I am writing a library for others to use and there are a few issues:

  1. I don't actually know if the type is a generic type or a regular type, I only know it is annotated with @id.
  2. I don't know where in the class hierarchy this field is located.

Here is the method I am using to get hold of all fields in the class hierarchy.

private Set<Field> getAllFields(final Object object) {
    final Set<Field> fields = new HashSet<>();
    fields.addAll(Arrays.asList(object.getClass().getDeclaredFields()));
    Class<?> superclass = object.getClass().getSuperclass();
    while (superclass != null) {
        fields.addAll(Arrays.asList(superclass.getDeclaredFields()));
        superclass = superclass.getSuperclass();
    }
    return fields;
}

I then filter this list for field annotated with @Id of which there should only be one.

Upvotes: 1

Views: 2450

Answers (1)

Buhake Sindi
Buhake Sindi

Reputation: 89199

Generics are not reified at run-time. This means the information is not present at run-time.

One way is to know the class type of the generic parameterized type, i.e., Class<T> entityClass.

There are couple of ways to achieve this. You can either have a default constructor and reflectively get the class type:

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

Or create a constructor, passing the the Generic class type on the constructor:

protected AbstractEntity(Class<T> entityClass) {
    this.entityClass = entityClass;
}

Then, your concrete entity class can pass its class type by default, like so:

public class MyClass extends AbstractEntity<Long> {

    public MyClass() {
        super(Long.class);
    }
}

Upvotes: 2

Related Questions