2008-10-13 89 views
43

所以,我明白以下不起作用,但是爲什麼不起作用?爲什麼我不能在具有多個邊界的類型參數中使用類型參數?

interface Adapter<E> {} 

class Adaptulator<I> { 
    <E, A extends I & Adapter<E>> void add(Class<E> extl, Class<A> intl) { 
     addAdapterFactory(new AdapterFactory<E, A>(extl, intl)); 
    } 
} 

add()方法給我一個編譯錯誤,「不能指定任何附加綁定適配器<Ë>當第一結合是一種類型的參數」(在Eclipse),或「類型參數不能被隨後的其他邊界」 (在IDEA中),請選擇。

顯然你只是不允許使用類型參數I那裏,在&之前,就是這樣。 (並且在你問之前,如果你切換他們,它是行不通的,因爲不能保證I不是一個具體的類。)但是爲什麼不呢?我瀏覽了Angelika Langer的常見問題,無法找到答案。

通常,當某些泛型限制看起來是任意的時,這是因爲您已經創建了類型系統實際上不能實施正確性的情況。但我不明白什麼情況會打破我在這裏要做的事情。我想說也許它與類型擦除後方法調度有關,但只有一個方法add(),所以它不像有任何歧義...

有人可以證明我的問題嗎?

回答

23

我也不確定爲什麼限制在那裏。您可以嘗試發送一封友好的電子郵件給Java 5 Generics(主要是Gilad Bracha和Neal Gafter)的設計人員。

我的猜測是他們只想支持絕對最小值intersection types(這是多邊界本質上是什麼),使語言不再比需要更復雜。交集不能用作類型註釋;一個程序員只能表達一個交點,當它出現在一個類型變量的上界時。

爲什麼這種情況下甚至支持?答案是多重邊界允許您控制擦除,這可以在生成現有類時保持二進制兼容性。正如Naftalin和Wadler在book第17.4解釋,一個max方法將邏輯上具有以下特徵:

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll) 

然而,這將刪除到:

public static Comparable max(Collection coll) 

不匹配的歷史簽名max,並導致老客戶中斷。 隨着多重限制,只有最左邊的結合被認爲是擦除,因此,如果max給出以下特徵:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 

然後其簽名的擦除變爲:

public static Object max(Collection coll) 

哪個等於泛型之前的max的簽名。

這似乎也合情合理Java的設計者只關心這個簡單的例子,並限制其他(更先進的)使用交集類型的,因爲他們只是不確定的複雜性,它可能帶來的。所以這個設計決定的原因並不需要是一個可能的安全問題(如問題所示)。

更多關於交叉點的類型和在upcoming OOPSLA paper泛型的限制的討論。

+0

實際上,如果多個邊界點是要控制擦除,那完全有意義,因爲我只是要擦除Object。 – 2008-10-13 13:11:46

1

這可能不回答根本問題,但只是想指出規範明確禁止它。谷歌搜索的錯誤消息,帶我去this blog entry,這進一步指向jls 4.4

結合的方案包括將類型變量,或者一個類或接口類型T接着可能進一步接口類型I1的,... ,在。

因此,如果您使用類型參數作爲綁定,則不能使用任何其他綁定,就像錯誤消息所述。

爲什麼限制?我不知道。

+0

因爲我可能是一類?僅僅因爲一個擴展我並不意味着我是一個接口(好吧,這將使一個接口),但是A可以很容易地的我,這是由規範 – 2008-10-13 11:50:44

+0

禁止的是什麼問題,如果我是一個類的子類?已知適配器是一個接口。 – 2008-10-13 12:21:48

9

下面是從JLS另一個報價:

綁定的形式被限制(僅第一元件可以是一個類或類型的變量,和僅一種類型的可變可能出現在約束)預先排除某些尷尬的情況出現

究竟是那些尷尬的情況,我不知道。爲取締這種

+1

也許他們也不知道? – Pacerier 2014-08-25 23:07:52

12

兩個可能的原因:

  1. 複雜性。 Sun bug 4899305表明,一個含鍵合的類型參數加上附加的參數化的類型將允許甚至更復雜的相互遞歸類型比已經存在。總之,Bruno's answer

  2. 指定非法類型的可能性。具體而言,extending a generic interface twice with different parameters。我不能拿出一個非人爲的例子,但:

    /** Contains a Comparator<String> that also implements the given type T. */ 
    class StringComparatorHolder<T, C extends T & Comparator<String>> { 
      private final C comparator; 
      // ... 
    } 
      
    void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }

現在holder.comparatorComparator<Integer>Comparator<String>。我不清楚這會給編譯器帶來多大的麻煩,但顯然不好。假設特別是Comparator有這樣的方法:

void sort(List<? extends T> list);

我們Comparator<Integer>/Comparator<String>混合動力車現在有兩種方法具有相同的擦除:

void sort(List<? extends Integer> list); 
void sort(List<? extends String> list);

這對這些類型的原因,你不能指定這樣的類型直接:

<T extends Comparator<Integer> & Comparator<String>> void bar() { ... }
java.util.Comparator cannot be inherited with different arguments: 
    <java.lang.Integer> and <java.lang.String>

由於<A extends I & Adapter<E>>讓你做同樣的事情,間接地,它出來了。

相關問題