如何获得间接实现的通用接口的实际类型参数?
我有一个参数化的接口,以许多不同的方式实现。在运行时,我需要弄清楚,给定一个实现该接口的任意对象,接口的实际类型参数是什么。如何获得间接实现的通用接口的实际类型参数?
下面就来说明问题的一个片段,并且中途试图解决它(also on ideone.com):
import java.util.*;
import java.lang.reflect.*;
interface Awesome<X> { }
class Base<E> implements Awesome<Set<E>> { }
class Child extends Base<List<Integer>> { }
class AwesomeExample {
public static void main(String[] args) {
Awesome<Set<List<Integer>>> x = new Child();
System.out.println(
((ParameterizedType)
Child.class.getGenericSuperclass()
).getActualTypeArguments()[0]
);
// prints "java.util.List<java.lang.Integer>"
System.out.println(
((ParameterizedType)
Base.class.getGenericInterfaces()[0]
).getActualTypeArguments()[0]
);
// prints "java.util.Set<E>"
investigate(x);
// we want this to print "Set<List<Integer>>"
}
static void investigate(Awesome<?> somethingAwesome) {
// how to do this?
}
}
它看起来像有在运行时足够的泛型类型的信息推断出:
Child extends Base<List<Integer>>
Base<E> implements Awesome<Set<E>>
,因此,我们可以把所有的点点滴滴,共同结论是:
Child implements Awesome<Set<List<Integer>>>
所以看起来这个问题是可以解决的,但它不是那么简单,因为我们不得不使用任意的类/接口层次结构。这是做到这一点的唯一方法吗?有一种更简单的方法吗?有人写过一个图书馆来做到这一点吗?
简短的回答是NO。我同意这是一个遗憾...... :( 原因是Java在编译阶段丢弃了类型参数,它们不存在于字节码中,它们只被编译器使用
为了解决你的问题,你不得不又加Class类型的另一种“常规”参数,并将其传递给构造函数当你创建基地的一个实例:
class Base<E> implements Awesome<Set<E>> {
private E type;
public Base(E type) {
this.type = type;
}
}
类型信息位于字节码中。所以你不正确。你可以在这个位置获得类型信息。谷歌超级类型的令牌来举个例子。 – 2010-12-23 09:03:38
编辑:你可能只是想看看使用:http://code.google.com/p/gentyref/
如果您可以保证Awesome<?>
的所有实现都不具有类型参数,则以下公司德应该让你开始[1]:
static void investigate(Object o) {
final Class<?> c = o.getClass();
System.out.println("\n" + c.getName() + " implements: ");
investigate(c, (Type[])null);
}
static void investigate(Type t, Type...typeArgs) {
if(t == null) return;
if(t instanceof Class<?>) {
investigate((Class<?>)t, typeArgs);
} else if(t instanceof ParameterizedType) {
investigate((ParameterizedType)t, typeArgs);
}
}
static void investigate(Class<?> c, Type...typeArgs) {
investigate(c.getGenericSuperclass(), typeArgs);
for(Type i : c.getGenericInterfaces()) {
investigate(i, typeArgs);
}
}
static void investigate(ParameterizedType p, Type...typeArgs) {
final Class<?> c = (Class<?>)p.getRawType();
final StringBuilder b = new StringBuilder(c.getName());
b.append('<');
Type[] localArgs = p.getActualTypeArguments();
if(typeArgs != null && typeArgs.length > 0) {
int i = 0, nextTypeArg = 0;
for(Type local : localArgs) {
if(local instanceof ParameterizedType) {
ParameterizedType localP = (ParameterizedType) local;
b.append(localP.getRawType()).append('<');
b.append(typeArgs[nextTypeArg++]);
b.append('>');
} else if(local instanceof TypeVariable) {
// reify local type arg to instantiated one.
localArgs[nextTypeArg] = typeArgs[nextTypeArg];
b.append(localArgs[nextTypeArg]);
nextTypeArg++;
} else {
b.append(local.toString());
}
b.append(", ");
i++;
}
if(typeArgs.length > 0) {
b.delete(b.length() - 2, b.length());
}
b.append('>');
} else {
String args = Arrays.toString(localArgs);
b.append(args.substring(1, args.length()-1)).append('>');
}
System.out.println(b);
investigate(c, localArgs);
}
但是,如果的Awesome<?>
或Base<E>
实例将进行,这种类型的信息将因擦除丢失。这可以四处工作,按照惯例,像这样的东西:
Awesome<?> awesome = new Base<Double>() {};
通知的{}
,这将创建一个新的匿名类实现(或在此扩展)Base<E>
。这个类将有它的类型参数可用于反射。
如果你害怕执行这一公约将是一个问题,你可以隐藏构造&只会暴露工厂方法:
class Base<E> implements Awesome<Set<E>> {
public static Base<Number> newNumberInstance() {
return new Base<Number>() {};
}
protected Base() {}
}
正如上面的代码还没有被完全测试,你可能想要做的那。这里的要点是,如果您的要求足够严格,您可以找到实际的类型参数。这是否适用于您的情况取决于您自己决定。
[1]它将打印出类所实现的所有接口&而不仅仅是Awesome
的类型参数。这可以改变,但我想我会去更一般的&让你解决具体问题。例如,你要测试这些,看看我的意思是:
investigate(new ArrayList<Integer>());
investigate(new ArrayList<String>() {}); // new anonymous ArrayList class
investigate("");
investigate(new Awesome<Comparable<?>>() {}); // new anonymous implementation of Awesome
继@ oconnor0的建议,这里是如何与gentyref做到这一点:
static Type investigate(Awesome<?> somethingAwesome) {
return GenericTypeReflector.getTypeParameter(somethingAwesome.getClass(), Awesome.class.getTypeParameters()[0]);
}
如果somethingAwesome.getClass()
可能也是通用的,首先将它通过GenericTypeReflector.addWildcardParameters
可能会很有用。
春天也有GenericTypeResolver.resolveTypeArgument(Class,Class)可以达到相同的结果。
我不知道实现这个库。我自己做了 - 这不是很愉快。我至少可以找到超类型令牌实现:http://code.google.com/p/google-gson/source/browse/trunk/src/main/java/com/google/gson/reflect/TypeToken.java ?r = 60&spec = svn89 – 2010-12-23 09:17:52