首頁  >  文章  >  Java  >  Java中泛型的協變

Java中泛型的協變

高洛峰
高洛峰原創
2016-11-22 17:56:441437瀏覽

在工作上遇到一個問題,用程式碼描述如下:

package test;import java.util.LinkedList;
import java.util.List;public class ListTest {
    public void func(List<Base> list) {
    }
    public static void main(String args[]) {
        ListTest lt = new ListTest();
        List<Derived> list = new LinkedList<Derived>();
        lt.func(list); // 编译报错    }
}class Base {
}class Derived extends Base {
}

這裡需要寫一個函數func,能夠以Base的list作為參數。原以為傳一個Derived的list也可以,因為Derived是Base的派生類,那Derived的list也應當是Base的list的派生類,結果編譯器報錯。

究其原因,在網路上查了一些資料:Java的泛型並非協變的。

泛型的協變和逆變都是術語,前者指能夠使用比原始指定的派生類型的派生程度更小(不太具體的)的類型,後者指能夠使用比原始指定的派生類型的派生程度較大(較具體的)的型別。

例如C#中的泛型就是支援協變的:

IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;

但是Java的泛型卻是不支援協變的,類似上面的程式碼在Java中無法通過編譯。

但有趣的是,Java中的陣列卻是支援協變,例如:

Integer[] intArray = new Integer[10]; 
Number[] numberArray = intArray;

總結:Java的泛型不支援協變,更多的是從型別安全的角度考慮。這種設計不是一定必須的,例如C#就沒有採用這種設計。只能說Java的設計者在易用性和型別安全性之間做了取捨。

最後回到最初的那個問題,要實現一個那樣的方法func,可以修改為:

public void func(List list) {
}

或者採用參數化類型:

public <T> void func(List<T> list) {
}

但是這樣也有問題,會模糊了func的參數類型。更好的方法是不改func,在傳參時就傳一個Base類型的List,這就要求在將元素加入這個List時就要轉型成Base類型。

PS:透過限制參數類型:

public void func(List<? extends Base> list) {
}


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn