我有一个参数化的界面,以多种不同的方式实现.在运行时,我需要弄清楚,给定一个实现该接口的任意对象,接口的实际类型参数是什么.
这是一个用来说明问题的片段,以及解决问题的中途尝试(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扩展Base< List< Integer>>
>基础< E>实现Awesome< Set< E>>
因此,我们可以把所有的点点滴滴放在一起得出结论:
> Child实现了Awesome< Set< List< Integer>>>
所以看起来这个问题是可以解决的,但并不是那么简单,因为我们必须使用任意的类/接口层次结构.这是唯一的方法吗?有更简单的方法吗?有人写过图书馆吗?
解决方法
编辑:您可能只想查看使用:
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<?>的实例化呢?或者基础< 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