1. 程式人生 > >java的協變性、逆變性、不變性

java的協變性、逆變性、不變性

    先看看官方的解釋:協變和逆變都是術語,前者指能夠使用比原始指定的派生型別的派生程度更大(更具體的)的型別,後者指能夠使用比原始指定的派生型別的派生程度更小(不太具體的)的型別。協變和逆變是指寬型別和窄型別在某種情況下(如引數、泛 型、返回值)替換或交換的特性。

 

    簡單地說A和B是型別,f表示型別轉換,≤表示子型別關係:

    協變:你可以用一個子類物件去替換相應的一個父類物件。如果A≤B 則f(A) ≤ f(B) 那麼 f是協變的 。如果能在泛型介面或者委託中保證,型別引數只能外部取出而不允許外部傳入。那麼就可以避免將型別引數作為引數傳入方法的情況。可以通過在型別引數前面加out來解決這個問題。

 

    逆變:你可以用一個父類物件去替換相應的一個父類物件。如果A≤B 則f(B) ≤ f(A) 那麼 f 是逆變的。如果能在泛型介面或者委託中保證,型別引數只能作為引數從外部傳入而不允許將其取出。那麼就不存在將型別引數作為返回值返回的情況了。可以通過在型別引數前面加in來解決這個問題。

 

    不變:對於不支援協變和逆變的情況稱為不變性。上面兩種都不成立,那麼f是無關的。

    來實際說明一下協變性和不可性在java中的具體體現。

    一、java的陣列就具有協變性

        陣列的協變性是指如果類Base是類Sub的基類,那麼Base[]就是Sub[]的基類。陣列是協變的導致了很多問題的出現,Object[]型別的引用可以指向一個String[]型別的物件。但是執行的時候是會報出如下異常的:Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer 試圖將錯誤型別的物件儲存到一個物件陣列時丟擲的異常。這樣導致陣列的型別並不安全。陣列則是在執行時做型別檢查,因為能夠記得陣列內部元素的具體型別。

 

 

   二、java的泛型就具有不變性

        泛型是不可變的,List<Base>不會是List<Sub>的基類,更不會是它的子類。為了遵守泛型型別安全原則,所以在泛型“編譯時進行型別檢查”特性決定了其不可協變。因為在泛型進行型別擦除,所以只能選擇在編譯期進行型別檢查,因為一旦執行起來這個型別就會被擦除掉,如果型別不一致編譯就不會通過。為了讓泛型更靈活也可以具備協變性,就可以使用萬用字元來模擬協變。