通配符的每個用法都有與其相關的不同類型。 (通常情況下,JLS是指此爲一個「清爽型」)。這是怎麼例如像這樣的編譯器錯誤的原理:
List<?> list0 = ... ;
List<?> list1 = ... ;
list0.add(list1.get(0)); // error
,因爲它是list0
和list1
是由編譯器給出不同的類型的情況下使得大部分
reference_type_of(List<?>) != reference_type_of(List<?>)
你可以開始看到如何適應如果您嘗試類似
{
List<?> list0 = ... ;
List<?> list1 = ... ;
test(list0, list1);
}
static <T> void test(List<T> list0, List<T> list1) {}
凡類型推斷編譯器會發出一個錯誤,實際上告訴我們一些關於它爲list0
和list1
生成的類型。
error: method test in class Ideone cannot be applied to given types;
test(list0, list1);
^
required: List<T>,List<T>
found: List<CAP#1>,List<CAP#2>
reason: no instance(s) of type variable(s) T exist so that
argument type List<CAP#2> conforms to formal parameter type List<T>
where T is a type-variable:
T extends Object declared in method <T>test(List<T>,List<T>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
期間capture conversion產生這些CAP#...
類型(我的粗體。強調)。它向我們展示的是,當檢查方法調用表達式時,list0
和list1
被賦予了彼此不同的類型。 (對於那些需要解釋的錯誤。這是因爲test
聲明稱,兩份名單必須有相同的T
)
所以,因爲我們現在知道,一個通配符被相關的引用類型,我們可以看到在像
List<?> empty = Collections.emptyList();
情況下的調用將被推斷爲像「新鮮的類型,其中上限是對象」。或者象徵性地我們可以說,編譯器可能會看到類似
// target type --> inferred invocation type
// v v
List<CAP#1> empty = Collections.<CAP#1>emptyList();
雖然:當然,我們一直在猜測一點點,因爲這完全取決於它如何實現這個編譯器。理論上,對於上述emptyList()
這樣微不足道的分配情況,它不需要做任何工作來生成正確的字節碼。
此外,我很抱歉,我不覺得今天spec掃描。基本上,這裏的類型推斷通過生成一組約束來演示方法調用應該或不應該編譯。在18.5.2中描述的算法以這種方式包含通配符。
它只是'?'。 –
'?'不是「任何類型」,它是「未知類型」。除此之外,奧利弗說。 – markspace
作爲一個切線,目前還不清楚你會用'List >'做什麼,因爲你不能放任何東西。 –