3 回答

TA貢獻(xiàn)2080條經(jīng)驗(yàn) 獲得超4個(gè)贊
有人說(shuō)這與類型和子類型之間的關(guān)系有關(guān),有人說(shuō)與類型轉(zhuǎn)換有關(guān),還有人說(shuō)它用于確定方法是被覆蓋還是被重載。
上述所有的。
本質(zhì)上,這些術(shù)語(yǔ)描述了類型轉(zhuǎn)換如何影響子類型關(guān)系。也就是說(shuō),如果A和B是類型,f則是類型轉(zhuǎn)換,并且≤子類型關(guān)系(即A ≤ B表示A是的子類型B),我們有
f是協(xié)變的,如果A ≤ B暗示f(A) ≤ f(B)
f是矛盾的,如果A ≤ B暗示f(B) ≤ f(A)
f 如果以上兩個(gè)都不成立,則是不變的
讓我們考慮一個(gè)例子。讓f(A) = List<A>哪里L(fēng)ist聲明
class List<T> { ... }
是f協(xié)變,逆變還是不變?協(xié)變意味著a List<String>是的子類型List<Object>,相反,a List<Object>是的子類型,List<String>并且不變都不是另一個(gè)的子類型,即List<String>和List<Object>是不可轉(zhuǎn)換的類型。在Java中,后者是正確的,我們說(shuō)(某種程度上是非正式的)泛型是不變的。
另一個(gè)例子。讓f(A) = A[]。是f協(xié)變,逆變還是不變?也就是說(shuō),String []是Object []的子類型,Object []是String []的子類型,還是兩者都不是子類型?(答案:在Java中,數(shù)組是協(xié)變的)
這仍然很抽象。為了更加具體,讓我們看一下Java中的哪些操作是根據(jù)子類型關(guān)系定義的。最簡(jiǎn)單的例子是分配。該聲明
x = y;
僅在時(shí)才編譯typeof(y) ≤ typeof(x)。也就是說(shuō),我們剛剛了解到
ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();
不會(huì)在Java中編譯,但是
Object[] objects = new String[1];
將。
子類型關(guān)系很重要的另一個(gè)示例是方法調(diào)用表達(dá)式:
result = method(a);
非正式地說(shuō),該語(yǔ)句是通過(guò)將a方法的值分配給方法的第一個(gè)參數(shù),然后執(zhí)行方法的主體,然后將方法的返回值分配給來(lái)評(píng)估的result。就像最后一個(gè)示例中的普通分配一樣,“右側(cè)”必須是“左側(cè)”的子類型,即,僅當(dāng)typeof(a) ≤ typeof(parameter(method))和時(shí),此語(yǔ)句才有效returntype(method) ≤ typeof(result)。也就是說(shuō),如果方法通過(guò)以下方式聲明:
Number[] method(ArrayList<Number> list) { ... }
以下任何表達(dá)式都不會(huì)編譯:
Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());
但
Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());
將。
子類型很重要的另一個(gè)示例是重載??紤]:
Super sup = new Sub();
Number n = sup.method(1);
哪里
class Super {
Number method(Number n) { ... }
}
class Sub extends Super {
@Override
Number method(Number n);
}
非正式地,運(yùn)行時(shí)會(huì)將其重寫為:
class Super {
Number method(Number n) {
if (this instanceof Sub) {
return ((Sub) this).method(n); // *
} else {
...
}
}
}
為了編譯標(biāo)記行,覆蓋方法的方法參數(shù)必須是覆蓋方法的方法參數(shù)的超類型,返回類型必須是覆蓋方法的子類型。從形式上來(lái)講,f(A) = parametertype(method asdeclaredin(A))必須f(A) = returntype(method asdeclaredin(A))至少是協(xié)變的,如果必須至少是協(xié)變的。
請(qǐng)注意上面的“至少”。這些是任何合理的靜態(tài)類型安全的面向?qū)ο缶幊陶Z(yǔ)言都將強(qiáng)制執(zhí)行的最低要求,但是編程語(yǔ)言可能會(huì)選擇更嚴(yán)格的標(biāo)準(zhǔn)。對(duì)于Java 1.4,在覆蓋方法(即parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B)),覆蓋)時(shí),參數(shù)類型和方法返回類型必須相同(類型擦除除外)。從Java 1.5開始,重寫時(shí)允許使用協(xié)變返回類型,即以下內(nèi)容將在Java 1.5中進(jìn)行編譯,但在Java 1.4中不進(jìn)行編譯:
class Collection {
Iterator iterator() { ... }
}
class List extends Collection {
@Override
ListIterator iterator() { ... }
}
我希望我覆蓋了所有內(nèi)容,或者更確切地說(shuō),是劃傷了表面。我仍然希望它將有助于理解類型差異的抽象但重要的概念。
添加回答
舉報(bào)