给定以下接口:
public interface GenericInterface<T> {
T getValue();
void setValue(T newVal);
}
以及以下impl:
public class FixedImpl implements GenericInterface<String> {
String value;
public FixedImpl(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String newVal) {
value = newVal;
}
}
我希望能够通过询问FixedImpl.class
来确定在FixedImpl
的情况下,String.class
是GenericInterface.T
的值。
我目前的想法:
- 在
GenericInterface
中查找一个返回<T>
的方法名称——在这种情况下,有"getValue"
- 遍历
FixedImpl.class
中使用相同名称声明的所有方法,并收集所有不同的返回类型 - 离
Object
最远的返回类型是我对GenericInterface.T
的值
但这个过程有几个问题:
- 它只适用于包含返回
<T>
的方法的泛型类型。使用setValue(T)
不能安全地实现同样的技巧,因为在Java源代码中可以通过参数/aarity进行方法重载。它只适用于T getValue()
,因为通过返回值重载是不可行的(除非我弄错了( - 它可能与Java8默认方法有奇怪的交互,或者在(仍然是泛型的(可能是抽象超类中实现泛型方法
- 这有点杂乱无章
有人能给我指一个更容易/更可靠的方法来获得同样的信息吗?我似乎找不到一个,但我想我应该问问嘟嘟声中的智者:(
注意:如果你想知道我为什么需要这个,那是因为我想用类似的硬编码类型参数,但POJO值而不是简单的字符串,用程序构建容器类的mock。
编辑:我最终找到了以下解决方案(在看到@stony-zhang的之前(:
public static <G> List<Class> getConcreteTypes(Class<? extends G> implClass, Class<G> genericClass) {
List<Class> concreteTypes = new ArrayList<Class>();
for (Type type : implClass.getGenericInterfaces()) {
if (!(type instanceof ParameterizedTypeImpl)) continue;
ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) type;
if (parameterizedType.getRawType() != genericClass) continue;
for (Object arg : parameterizedType.getActualTypeArguments()) {
if (!(arg instanceof Class))
throw new IllegalArgumentException("Class " + implClass + " not concrete for generic type " + genericClass);
concreteTypes.add((Class) arg);
}
}
return concreteTypes;
}
您可以通过以下方式获得T的类,在接口中添加方法getMessageClass((,在FixedImpl中添加实现的方法
@SuppressWarnings("rawtypes")
public Class getMessageClass() {
int index =0; //In the case, you only have a generic type, so index is 0 to get the first one.
Type genType = getClass().getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
throw new RuntimeException("Index outof bounds");
}
if (!(params[index] instanceof Class)) {
return Object.class;
}
return (Class) params[index];
}
在这种情况下,如果您有多个子类,要使用它,请创建一个抽象类来实现接口GenericInterface,然后所有子类都从新的抽象类扩展
public class abstract abstractImpl<T> implements implements GenericInterface<T> {
@SuppressWarnings("rawtypes")
@Override
public Class getMessageClass() {
...............
}
}
记住类型擦除。在运行时,不再有关于泛型的类型信息,除非您自己指定。这就是你应该做的。把这个添加到你的界面:
Class<T> getTypeOfT();
并将其添加到您的FixedImpl
:中
@Override
public Class<String> getTypeOfT()
{
return String.class;
}
这样,您就可以在GenericInterface<T>
实现中始终调用getTypeOfT()
,并了解您正在处理的类型。
我不认为你能得到可靠的结果,因为类型擦除:
- 将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则替换为Object。因此,生成的字节码只包含普通的类、接口和方法
- 如有必要,插入类型强制转换以保持类型安全性
- 生成桥接方法以保留扩展泛型类型中的多态性
您使用返回的对象类型的方法一开始可能看起来不错,但除了您指出的问题之外,(在运行时(没有办法知道The return type farthest from Object is my value for GenericInterface.T.
我的建议是使用某种配置XML,这种配置XML可以在构建时基于java源代码生成(使用Ant等构建工具(,然后用于创建Mock对象,或者您可以简单地在构建时根据源代码生成测试。
如果你不介意为了测试而更改运行时代码,Jan Doereenhaus的回答建议了一种简单的硬编码机制来检索类型
编辑:
考虑以下场景:
public class FixedImpl implements GenericInterface<SomeClass> {
@Override
public SomeClass getValue() {
return new SomeClass();
}
}
public class FixedImpl2 extends FixedImpl {
@Override
public SomeClass getValue()
{
return new SomeSubClass();
}
}
从这个例子中,您可以看到FixedImpl的子类能够返回T
的子类(它位于Object
的继承层次结构的下游(