我有接口,带有泛型返回类型的方法,并且在运行时有一些间接实现该接口的类实例。现在我想找出使用反射的每个实现的实际返回类型。
(我的想法是使用这种机制来定义使用接口的策略,并在运行时从一组策略实现中找到匹配的策略(特定的返回类型),而不必引入冗余的辅助方法暴露类型)。
更具体地说,让我们考虑以下情况:
private interface DAO <I extends Serializable, E> {
public E getById (I id);
}
private abstract class AbstractDAO <T> implements DAO<Integer, T> {
@Override
public T getById (Integer id) {
// dummy implementation, just for this example
return null;
}
}
private class PersonDAO extends AbstractDAO<Person> {
}
private class PersonDAOExtension extends PersonDAO {
}
在运行时,我想找出给定类(PersonDAOExtension.class
)将为方法getById(..)
(预期为:Person.class
)返回哪种类型。
使用反射,我可以找出从此方法返回的通用Type
。在这种情况下,它是TypeVariable
(但也可能是Class
,如果层次结构中的任何类指定了协变返回类型):
Method method = PersonDAOExtension.class.getMethod("getById", Integer.class);
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof TypeVariable<?>) {
TypeVariable<?> typeVariable = (TypeVariable<?>) genericReturnType;
typeVariable.getName(); //results in "T"
}
我认为解析实际类型意味着递归到超类和接口并翻译原始(parameterizedType.getRawType()
)和实际(parameterizedType.getActualTypeArguments()
)在找到所需的类型名称之前,为任何参数化类型键入参数。
之前有没有人这样做过,也许有些代码片段可以帮我实现这个目标?非常感谢提前:))
提示:我能够在运行时使用反射提取以下信息,因此保留原始和实际类型信息:
private abstract interface DAO<I, E>
private abstract class AbstractDAO<T> extends Object implements DAO<Integer, T> [raw type:DAO<I, E>]
private class PersonDAO extends AbstractDAO<Person> [raw type:AbstractDAO<T>]
private class PersonDAOExtension extends PersonDAO
答案 0 :(得分:9)
我终于能够找到一个解决方案,递归到超类和接口,用传递的类型参数替换类型变量,直到达到所需的基类:
/**
* Resolves the actual generic type arguments for a base class, as viewed from a subclass or implementation.
*
* @param <T> base type
* @param offspring class or interface subclassing or extending the base type
* @param base base class
* @param actualArgs the actual type arguments passed to the offspring class
* @return actual generic type arguments, must match the type parameters of the offspring class. If omitted, the
* type parameters will be used instead.
*/
public static <T> Type[] resolveActualTypeArgs (Class<? extends T> offspring, Class<T> base, Type... actualArgs) {
assert offspring != null;
assert base != null;
assert actualArgs.length == 0 || actualArgs.length == offspring.getTypeParameters().length;
// If actual types are omitted, the type parameters will be used instead.
if (actualArgs.length == 0) {
actualArgs = offspring.getTypeParameters();
}
// map type parameters into the actual types
Map<String, Type> typeVariables = new HashMap<String, Type>();
for (int i = 0; i < actualArgs.length; i++) {
TypeVariable<?> typeVariable = (TypeVariable<?>) offspring.getTypeParameters()[i];
typeVariables.put(typeVariable.getName(), actualArgs[i]);
}
// Find direct ancestors (superclass, interfaces)
List<Type> ancestors = new LinkedList<Type>();
if (offspring.getGenericSuperclass() != null) {
ancestors.add(offspring.getGenericSuperclass());
}
for (Type t : offspring.getGenericInterfaces()) {
ancestors.add(t);
}
// Recurse into ancestors (superclass, interfaces)
for (Type type : ancestors) {
if (type instanceof Class<?>) {
// ancestor is non-parameterized. Recurse only if it matches the base class.
Class<?> ancestorClass = (Class<?>) type;
if (base.isAssignableFrom(ancestorClass)) {
Type[] result = resolveActualTypeArgs((Class<? extends T>) ancestorClass, base);
if (result != null) {
return result;
}
}
}
if (type instanceof ParameterizedType) {
// ancestor is parameterized. Recurse only if the raw type matches the base class.
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?>) {
Class<?> rawTypeClass = (Class<?>) rawType;
if (base.isAssignableFrom(rawTypeClass)) {
// loop through all type arguments and replace type variables with the actually known types
List<Type> resolvedTypes = new LinkedList<Type>();
for (Type t : parameterizedType.getActualTypeArguments()) {
if (t instanceof TypeVariable<?>) {
Type resolvedType = typeVariables.get(((TypeVariable<?>) t).getName());
resolvedTypes.add(resolvedType != null ? resolvedType : t);
} else {
resolvedTypes.add(t);
}
}
Type[] result = resolveActualTypeArgs((Class<? extends T>) rawTypeClass, base, resolvedTypes.toArray(new Type[] {}));
if (result != null) {
return result;
}
}
}
}
}
// we have a result if we reached the base class.
return offspring.equals(base) ? actualArgs : null;
}
像魅力一样:
resolveActualTypeArgs(PersonDAOExtension.class, DAO.class)
结果为Integer
,Person
resolveActualTypeArgs(AbstractDAO.class, DAO.class)
结果为Integer
,T
resolveActualTypeArgs(LinkedList.class, Iterable.class, String.class)
结果为String
我现在可以使用它来找出一组给定的DAO实现可以读取人员:
List<DAO<?, ?>> knownDAOs = ...
for (DAO<?, ?> daoImpl : knownDAOs) {
Type[] types = resolveActualTypeArgs(daoImpl.getClass(), DAO.class);
boolean canReadPerson = types[1] instanceof Class<?> && Person.class.isAssignableFrom((Class<?>) types[1]);
}
无论我是通过new PersonDAOExtension()
,new PersonDAO()
还是new AbstractDAO<Person>{}
,这都有效。
答案 1 :(得分:9)
我能够使用Google Guava的TypeToken类在一行中确定方法的泛型返回类型:
TypeToken.of(PersonDAOExtension.class)
.resolveType(PersonDAOExtension.class.getMethod("getById", Integer.class).getGenericReturnType())
.getRawType()
或者,如果要获取类的泛型类型(就像在接受的答案中所做的那样),而不是方法的返回类型,则可以执行以下操作:
TypeToken.of(PersonDAOExtension.class)
.resolveType(AbstractDAO.class.getTypeParameters()[0])
.getRawType()
这两个解决方案都按预期返回Person.class。
根据您对已接受答案的评论,您似乎只想知道给定的DAO是否可以接受人员。这也适用于API:
(new TypeToken<DAO<?, Person>>() {})
.isSupertypeOf(TypeToken.of(PersonDAOExtension.class))
对这个和其他Guava反射实用程序on the Guava site的功能有一个很好的解释。
答案 2 :(得分:0)
之前我遇到过类似的问题。在我的解决方案中,抽象类有一个名为
的方法 static void register(Class<? extends DAO> clazz, ? extends DAO daoInstance)
。
在抽象类中,我有一个Map
,它存储了对实例的引用。我使用单例,但如果您有多个实例,则可以使用多图。使用这种技术,你可以摆脱反射,你将拥有你注册的所有实现的Set
及其类。
如果您需要更多信息,也可以注册一些pojo类:
public class DaoData{
private Class<? extends DAO> daoClass;
private Class<?> someArbitraryTypeClass;
// ...
}
static void register(DaoData daoData, ? extends DAO daoInstance)
我知道这不是最好的解决方案,但很简单并完成工作。