2014-03-18 88 views
1

所以我有一個Tuple2類,如下所示:類型推斷奇人異

public final class Tuple2<T1, T2> { 
    private final T1 mItem1; 
    private final T2 mItem2; 

    public T1 getItem1() { return mItem1; } 
    public T2 getItem2() { return mItem2; } 

    public Tuple2(final T1 pItem1, final T2 pItem2) { 
     mItem1 = pItem1; 
     mItem2 = pItem2; 
    } 

    public static <TItem1, TItem2> Tuple2<TItem1, TItem2> 
    Create(final TItem1 pItem1, final TItem2 pItem2) { 
     return new Tuple2<>(pItem1, pItem2); 
    } 
} 

,我試圖實例Tuple2List S,但類型推斷似乎辦法不可思議。我希望我可以做這樣的事情:

// doesn't work 
// note that Type2 extends Type1, and Type3 extends Type2 
final List<Tuple2<String, Class<?>>> list = Arrays.asList(
    Tuple2.Create("1", Type1.class), 
    Tuple2.Create("2", Type2.class), 
    Tuple2.Create("3", Type3.class) 
); 

// still doesn't work 
final List<Tuple2<String, Class<? extends Type1>>> list = Arrays.asList(
    Tuple2.Create("1", Type1.class), 
    Tuple2.Create("2", Type2.class), 
    Tuple2.Create("3", Type3.class) 
); 

無論這些工作,如果我投來Class<?>Class<? extends Type1>無論是。

它所需要我做的卻是:

final List<Tuple2<String, ? extends Class<? extends Type1>>> list = Arrays.asList(
    Tuple2.Create("1", Type1.class), 
    Tuple2.Create("2", Type2.class), 
    Tuple2.Create("3", Type3.class) 
); 

不過,如果我想定義這些Tuple2 S的一個參考我寫的:

final Tuple2<String, ? extends Class<? extends Type1>> item = list.get(0); 

這是一個醜陋的類型名稱...有沒有一種方法來簡化這個?爲什麼它必須是Tuple2「的東西,擴展Class的東西,延伸Type1」,而不是簡單地「Class的東西」?

我發現的唯一的簡單的方法是使用Class原始類型,這似乎是氣餒,需要一些鑄造:

final List<Tuple2<String, Class>> list = Arrays.asList(
    Tuple2.Create("1", (Class)Type1.class), 
    Tuple2.Create("2", (Class)Type2.class), 
    Tuple2.Create("3", (Class)Type3.class) 
); 
final Tuple2<String, Class> item = list.get(0); 

回答

1

這是老generic invariance再次撫養它的頭。

每次調用Create,該類型推斷爲:

<String, Class<Type1>> 
<String, Class<Type2>> 
<String, Class<Type3>> 

正如你可能知道,一個List<Dog>不是List<Animal>和概念同樣適用在這裏。 A Tuple2<String, Class<Type1>>不是Tuple2<String, Class<?>>,而是Tuple2<String, ? extends Class<?>>

所以三個推斷Tuple2類型:

Tuple2<String, Class<Type1>> 
Tuple2<String, Class<Type2>> 
Tuple2<String, Class<Type3>> 

他們所共有的類型是Tuple2<String, ? extends Class<? extends Type1>>

據我知道解決這個問題的唯一方法是提供一種見證每個呼叫:

final List<Tuple2<String, Class<?>>> list = Arrays.asList(
    Tuple2.<String, Class<?>>Create("1", Type1.class), 
    Tuple2.<String, Class<?>>Create("2", Type2.class), 
    Tuple2.<String, Class<?>>Create("3", Type3.class) 
); 

這應該工作。

看起來這應該工作:

final List<Tuple2<String, Class<?>>> list = Arrays.asList(
    Tuple2.Create("1", (Class<?>)Type1.class), 
    Tuple2.Create("2", (Class<?>)Type2.class), 
    Tuple2.Create("3", (Class<?>)Type3.class) 
); 

不過,這並不編譯。編譯器確實捕獲轉換並推斷Class<?>Class<capture of ?>。所以推斷的類型你最終是這樣的:

<String, Class<capture #1 of ?>> 
<String, Class<capture #2 of ?>> 
<String, Class<capture #3 of ?>> 

而仍然是三個不同的類型彼此。

我會老實說:我不知道它爲什麼會這樣工作,就是這樣。我一直無法找到明確的答案。 The spec says this

  • 如果T 的形式爲一個通配符類型參數[...],則S 新鮮類型變量[...]。

這似乎暗示capture of ?被認爲是一種新的類型,並從?不同。

在我看來,這只是推論相對於通配符作爲版本間隙7

+0

感謝,這不是那樣乾淨,我想它,但它是稍微好一點。 (儘管我使用靜態方法來獲得類型推斷,但是如果這是我能得到的最好的結果,我也可以在這種情況下使用構造函數)。爲什麼我無法將'TypeX.class'投到'Class '? –

+0

查看我的編輯:不幸的是,我並沒有很好的答案,爲什麼確實如此,但我給了我理解。 – Radiodef