2010-12-23 117 views
7

我有一个参数化的接口,以许多不同的方式实现。在运行时,我需要弄清楚,给定一个实现该接口的任意对象,接口的实际类型参数是什么。如何获得间接实现的通用接口的实际类型参数?

下面就来说明问题的一个片段,并且中途试图解决它(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>>>

所以看起来这个问题是可以解决的,但它不是那么简单,因为我们不得不使用任意的类/接口层次结构。这是做到这一点的唯一方法吗?有一种更简单的方法吗?有人写过一个图书馆来做到这一点吗?

+0

我不知道实现这个库。我自己做了 - 这不是很愉快。我至少可以找到超类型令牌实现: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

回答

-1

简短的回答是NO。我同意这是一个遗憾...... :( 原因是Java在编译阶段丢弃了类型参数,它们不存在于字节码中,它们只被编译器使用

为了解决你的问题,你不得不又加Class类型的另一种“常规”参数,并将其传递给构造函数当你创建基地的一个实例:

class Base<E> implements Awesome<Set<E>> { 
    private E type; 
    public Base(E type) { 
     this.type = type; 
    } 
} 
+2

类型信息位于字节码中。所以你不正确。你可以在这个位置获得类型信息。谷歌超级类型的令牌来举个例子。 – 2010-12-23 09:03:38

4

编辑:你可能只是想看看使用: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 
0

继@ oconnor0的建议,这里是如何与gentyref做到这一点:

static Type investigate(Awesome<?> somethingAwesome) { 
    return GenericTypeReflector.getTypeParameter(somethingAwesome.getClass(), Awesome.class.getTypeParameters()[0]); 
} 

如果somethingAwesome.getClass()可能也是通用的,首先将它通过GenericTypeReflector.addWildcardParameters可能会很有用。

春天也有GenericTypeResolver.resolveTypeArgument(Class,Class)可以达到相同的结果。

相关问题