2011-06-15 42 views
7

的類型參數我有一個類Foo<T, U>與下面的構造:獲取參數化類

public Foo() { 
    clazz = Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]; 
} 

我在構造函數中做的是獲得類的說法U的。我需要它,因爲我使用它來實例化該類。

問題是,當我有一個Foo的子類時,它不起作用,它不是它的直接子類。讓我舉一個例子。

我有班級Bar<T> extends Foo<T, Class1>。這裏,Class1不是一個變量,而是一個類。

我也有類Baz extends Bar<Class2>Class2也是一個類,不是一個變量。

問題是,當我嘗試實例化BazBaz -> Bar<Class2> -> Foo<T, Class2>)時,它失敗。我得到一個ArrayIndexOutOfBoundsException,因爲getActualTypeArguments()返回一個數組只包含類Class2(大小1),我試圖獲得數組的第二個元素。那是因爲我得到了Bar<Class2>的參數,而不是Foo的參數。

我想要的是以某種方式修改Foo構造函數我可以在參數U中獲得類,如果我實例化的類是直接子類,則無關緊要。我認爲我應該可以在類的層次結構中繼續前進,直到到達Foo類爲止,將其轉換爲ParameterizedType並獲取參數,但我找不到方法。

有什麼想法?

在此先感謝。

回答

3

這是一個不錯的方法 - 它是真正獲得在Java字節碼內編譯的泛型類型信息的唯一方法。在Java中泛型的其他方面只是類型擦除。看到下面看來是一個工作解決方案。有四種情況:

  1. 一個相當複雜的繼承層次
  2. 你的產業情況
  3. 類本身(clazznull
  4. 的子類,不提供實際值(clazz = null

這是代碼(Test.java):

import java.lang.reflect.*; 
import java.util.*; 

class A<T1, T2> 
{ 
    Class<T2> clazz; 

    A() 
    { 
    Type sc = getClass().getGenericSuperclass(); 
    Map<TypeVariable<?>, Class<?>> map = new HashMap<TypeVariable<?>, Class<?>>(); 
    while (sc != null) 
    { 
     if (sc instanceof ParameterizedType) 
     { 
     ParameterizedType pt = (ParameterizedType) sc; 
     Type[] ata = pt.getActualTypeArguments(); 
     TypeVariable[] tps = ((Class) pt.getRawType()) 
      .getTypeParameters(); 
     for (int i = 0; i < tps.length; i++) 
     { 
      Class<?> value; 
      if (ata[i] instanceof TypeVariable) 
      { 
      value = map.get(ata[i]); 
      } 
      else 
      { 
      value = (Class) ata[i]; 
      } 
      map.put(tps[i], value); 
     } 
     if (pt.getRawType() == A.class) 
     { 
      break; 
     } 
     if (ata.length >= 1) 
     { 
      sc = ((Class) pt.getRawType()).getGenericSuperclass(); 
     } 
     } 
     else 
     { 
     sc = ((Class) sc).getGenericSuperclass(); 
     } 
    } 

    TypeVariable<?> myVar = A.class.getTypeParameters()[1]; 
    clazz = map.containsKey(myVar) ? (Class<T2>) map.get(myVar) : null; 
    } 
} 

class Bar<T> extends A<T, String> {} 
class Baz extends Bar<Integer> {} 

class A2<T3, T1, T2> extends A<T1, T2> { } 
class B<T> extends A2<Long, String, T> { } 
class C extends B<Integer> { } 
class D extends C { } 

class Plain<T1, T2> extends A<T1, T2> {} 

public class Test 
{ 
    public static void main(String[] args) 
    { 
    new D(); 
    new Baz(); 
    new A<String, Integer>(); 
    new Plain<String, Integer>(); 
    } 
} 
3

我創建了一個方法,它將返回一個參數化泛型的類型數組,其中'null'表示仍然存在的參數。我對你的Foo層次結構進行了測試,以及使用類「A」的其他答案中提供的示例。

編輯:在我沒有考慮如何重新排序類型會影響綁定。在新方法中,傳入你想要的參數的超類和基類。當我迭代超類時,我通過比較參數名稱來收集類型參數,這些類型參數是從前一個基類向前映射的。我希望這很清楚。

public <S, B extends S> Class[] findTypeParameters(Class<B> base, Class<S> superClass) { 
    Class[] actuals = new Class[0]; 
    for (Class clazz = base; !clazz.equals(superClass); clazz = clazz.getSuperclass()) { 
     if (!(clazz.getGenericSuperclass() instanceof ParameterizedType)) 
      continue; 

     Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments(); 
     Class[] nextActuals = new Class[types.length]; 
     for (int i = 0; i < types.length; i++) 
      if (types[i] instanceof Class) 
       nextActuals[i] = (Class) types[i]; 
      else 
       nextActuals[i] = map(clazz.getTypeParameters(), types[i], actuals); 
     actuals = nextActuals; 
    } 
    return actuals; 
} 

private Class map(Object[] variables, Object variable, Class[] actuals) { 
    for (int i = 0; i < variables.length && i < actuals.length; i++) 
     if (variables[i].equals(variable)) 
      return actuals[i]; 
    return null; 
} 

@Test 
public void findsTypeOfSuperclass() throws Exception { 
    assertThat(findTypeParameters(A4.class, A1.class), arrayContaining(Integer.class, Boolean.class, String.class)); 
    assertThat(findTypeParameters(B4.class, A1.class), arrayContaining(Integer.class, Boolean.class, String.class)); 
    assertThat(findTypeParameters(C2.class, A1.class), arrayContaining(Integer.class, null, null)); 
    assertThat(findTypeParameters(D4.class, A1.class), arrayContaining(Integer.class, null, String.class)); 
} 

class A1<T1, T2, T3> {} 
class A2<T3, T2> extends A1<Integer, T2, T3> {} 
class A3<T2> extends A2<String, T2> {} 
class A4 extends A3<Boolean> {} 

class B2<X, T3, Y, T2, Z> extends A1<Integer, T2, T3> {} 
class B3<X, T2, Y, Z> extends B2<X, String, Y, T2, Z> {} 
class B4<X> extends B3<X, Boolean, X, X> {} 

class C2<T2, T3> extends A1<Integer, T2, T3> {} 

class D2<T2> extends A1<Integer, T2, String> {} 
class D3 extends D2 {} 
class D4 extends D3 {} 
+1

感謝您的回覆:)。它對我有用。我意識到的一點是,當你改變參數的順序時,你沒有考慮到這種情況,例如'Bar extends Foo '。當你將參數類放在數組中的第一個空位時,在這個例子中,你會得到錯誤的類型。你不是嗎? – drumkey 2011-06-15 23:55:30