2014-01-11 47 views
21

以下類定義了兩種方法,這兩種方法直觀地具有相同的功能。每個函數都被調用了兩個類型爲List<? super Integer>的列表和一個布爾值,它指定應將哪些列表分配給局部變量。爲什麼三元運算符不像帶有有界通配符的泛型?

import java.util.List; 

class Example { 
    void chooseList1(boolean choice, List<? super Integer> list1, List<? super Integer> list2) { 
     List<? super Integer> list; 

     if (choice) 
      list = list1; 
     else 
      list = list2; 
    } 

    void chooseList2(boolean choice, List<? super Integer> list1, List<? super Integer> list2) { 
     List<? super Integer> list = choice ? list1 : list2; 
    } 
} 

javac 1.7.0_45chooseList1是有效的,而chooseList2不是。它抱怨:

java: incompatible types 
    required: java.util.List<? super java.lang.Integer> 
    found: java.util.List<capture#1 of ? extends java.lang.Object> 

我知道,查找包含三元運算符(… ? … : …)的表達式類型的規則是相當複雜的,但據我瞭解他們,它選擇了最具體的類型,這兩種第二個和第三個參數可以在沒有顯式轉換的情況下轉換。在這裏,這應該是List<? super Integer> list1,但它不是。

我希望看到一個解釋,爲什麼不是這種情況,最好是參考Java語言規範,並直觀地解釋如果沒有阻止會出現什麼問題。

+0

@BrianRoach我不認爲這是一個確切的重複,而密切相關。它涉及將條件運算符應用於通用方法調用的推斷返回類型,這適用於通配符捕獲泛型類型。 –

回答

13

此答案適用於爪哇7.

Java語言規範指出以下有關conditional operator (? :)

否則,第二和第三操作數分別爲類型S1和S2 的。設T1爲將拳擊 轉換爲S1所得到的類型,並且令T2爲將 將拳擊轉換爲S2所得到的類型。

條件表達式的類型是對lub(T1,T2)(§15.12.2.7)應用 捕獲轉換(第5.1.10節)的結果。

在表達式

List<? super Integer> list = choice ? list1 : list2; 

T1List<capture#1? super Integer>T2List<capture#2? super Integer>。這兩個都有較低的界限。

This article詳細瞭解如何計算lub(T1, T2)(或join function)。讓我們從那裏

<T> T pick(T a, T b) { 
    return null; 
} 

<C, A extends C, B extends C> C test(A a, B b) { 
    return pick(a, b); // inferred type: Object 
} 

void tryIt(List<? super Integer> list1, List<? super Integer> list2) { 
    test(list1, list2); 
} 

一個例子,如果您使用的IDE和懸停在test(list1, list2),你會發現返回類型爲

List<? extends Object> 

這是Java的類型推斷可以做到最好。如果list1List<Object>list2List<Number>,則唯一可接受的返回類型是List<? extends Object>。由於必須覆蓋此案例,因此該方法必須始終返回該類型。

同樣在

List<? super Integer> list = choice ? list1 : list2; 

lub(T1, T2)再次List<? extends Object>其捕獲轉換List<capture#XX of ? extends Object>

最後,類型List<capture#XX of ? extends Object>的引用不能分配給類型爲List<? super Integer>的變量,因此編譯器不允許它。

+1

+1偉大的鏈接和解釋。 –

+0

我還沒有理解如何計算兩種類型的'lub()'。但是爲什麼'List <?擴展Object>'「Java的類型推斷可以做的最好的」?如果結果是「List <?」,它會更有用。超級整數>「,但是會出現什麼樣的複雜情況,即在什麼情況下會打破安全類型或使其他方法不可行? – Feuermurmel

+0

@Feuermurmel通過聲明'?超級整數「,則聲明泛型類型的下限。這意味着實際列表可以是'列表','列表','列表'中的任何一個。 ''的類型? :'表達式由兩個參數的最小上限決定。他們都是'List <?超級整數「,但由於下限,可以是」列表「,另一個可以是」列表「。 Java在編譯時無法確定,所以結果必須是某種類型的Object,即Object的子類型。 '名單<?擴展Object>'。 –

1

時間流逝和Java的變化。我很高興地通知您,自Java 8以來,可能由於引入了"target typing",Feuermurmels示例編譯時沒有任何問題。

relevant section of the JLS的當前版本說:

由於參考條件表達式可以是聚表情,他們可以「傳下」背景下它們的操作數。

...

它還允許使用額外的信息來提高泛型方法調用的類型檢查。到Java SE 8之前,這個作業良好,鍵入:

List<String> ls = Arrays.asList();

但這並不:上面

List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");

規則允許考慮兩個作業以及類型的。

這也是有趣的是,以下,從索蒂里奧斯Delimanolis代碼衍生的不編譯:

void tryIt(List<? super Integer> list1, List<? super Integer> list2) { 
    List<? super Integer> l1 = list1 == list2 ? list1 : list2; // Works fine 
    List<? super Integer> l2 = test(list1, list2); // Error: Type mismatch 
} 

這表明,現有的資料計算類型返回類型下界時test是不同的從那個條件運算符的類型。爲什麼這是我不知道的情況,它本身可能是一個有趣的問題。

我使用jdk_1.8.0_25。