Generics and reflection in Java

2019-07-16 13:14发布

问题:

This is probably a very basic question, but I'm really new to generics in Java and I'm having a hard time altering my thought process from the way things are done in C#, so bear with me.

I'm trying to build a generic repository in Java. I've created an IRepository interface that looks like this:

public interface IRepository<T extends IEntity>

And a Repository class that looks like this:

public class Repository<T extends IEntity> implements IRepository<T>

Now, from within the constructor of my Repository class, I'd like to be able to "divine" the exact type of T. For example, if I instantiated a repository like this:

IRepository<MyClass> repo = new Repository<MyClass>();

I'd like to know that T is actually MyClass. This is trivial in C#, but obviously generics are a totally different beast in Java and I can't seem to find anything that would help me do this.

回答1:

Java uses type erasure, so the specific information is lost at runtime - you only know that this type is generic, not what's the specific argument you've supplied at compile time.



回答2:

You can always add the actual type of T in your constructor, like so:

public class Repository<T> implements IRepository<T>
  public Repository(Class<T> type) {
  }
}

and instantiate like

IRepository<MyClass> repo = new Repository<MyClass>(MyClass.class);


回答3:

I've done something similar (also to implement a Repository/Registry pattern) and with a bit of work you can actual find out the type. Note however I wasn't doing this with interfaces, but with a base class, and also note that it took a bit of trial and error to arrive at a solution that worked. This code was running on the Sun JVM so it may be that I've stumbled into an area that is JVM specific.

Also - as another comment mentioned, just because you can do this, doesn't mean you necessarily should :)

import java.lang.reflect.ParameterizedType;
...
public static Class<?> type(Object target) {
    Class base = target.getClass();
    while (base != null) {
        Object gsuper = base.getGenericSuperclass();
        if (gsuper != null && gsuper instanceof ParameterizedType) {
            Object o = ((ParameterizedType) gsuper).getActualTypeArguments()[0];
            if (o instanceof Class) {
                return (Class<?>) o;
            }
        }
        base = base.getSuperclass();
    }

    return null;
}

I used this from my BaseRepository class like:

Class<?> type = type(SomeRepository);

where:

public class SomeRepository extends Repository<MyEntity> {
... 
}