3 回答

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