2010-12-23 113 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)可以達到相同的結果。

相關問題