泛型與型別安全
1. 基礎泛型
//定義泛型類,介面的定義和類一樣 class A<T,P extends Number> { T t; P p;// extends 限定泛型的範圍,等於或者是extends的子類;萬用字元才有extends和super public <M> M doSomething(M params) {} }
2. 協變與逆變與不變
- 協變
簡單來說即:
A extends B==>A[] extends B[] B[] bs = new A[2];
Java中的陣列是協變的
-
逆變
與協變相對,逆轉了型別關係
-
不變
Java 語言中的泛型基本上完全在編譯器中實現,由編譯器執行型別檢查和型別推斷,然後生成普通的非泛型的位元組碼。這種實現技術稱為擦除(erasure) (編譯器使用泛型型別資訊保證型別安全,在生成位元組碼之前將其清除,替換為非泛型上界)
由於泛型擦除的原因,Java中的泛型是不變的,因此在使用泛型的地方(比如集合)中
List<B> bls = new ArrayList<A>();//不合法
編譯器檢查不通過,這是因為B可以有其它的子類比如C,由於bls的引用指定的型別是B,所以
C extends B bls.add(new C())
是合法的,假設bls指向的例項指定的型別是A成立,而A與C並沒有直接聯絡,則編譯器認為上述新增會導致型別不安全。
為了解決泛型的不變,就需要用到第三部分的萬用字元?與extends、super。
3. 萬用字元? 與 extends 與 super
有以下合法的寫法
//萬用字元?在單獨使用時可以看作是? extends Object List<?> l0= new ArrayList<Object>(); //extends包含指向類及其子類 List<? extends Number> l1= new ArrayList<Number>(); List<? extends Number> l2= new ArrayList<Integer>(); //super包含指向類及其父類 List<? super Integer> l3 = new ArrayList<Integer>(); List<? super Integer> l4 = new ArrayList<Number>();
儘管如上所寫都能夠正確的通過編譯器的檢查,但是在實際使用時卻並不是這麼順利,比如
? extends xxx使得泛型具有協變的特性
l1.add(new Integer(1));//無法編譯 Number n = l1.get(0);//可以獲取到
當向l1中add一個元素,不管是任何型別的元素,都是不合法的,因為泛型下界無法確定,比如Integer和Double同是Number的子類,但是一個指向List<Integer>的例項add一個Double型別的元素顯然是不合法的;當從l1中get一個元素,泛型上界確定是Number
? super xxx使得泛型具有逆變的特性
l3.add(new Integer(1));//可以編譯 Object o = l3.get(0);//可以獲取到
想要向l3中add一個元素,泛型下界確認為Integer,那麼這個元素必須是Integer及其子類的型別;當從l3中get一個元素時,泛型上界確定是Object
總結一點就是:寫入需要有型別下界,而讀出需要有型別上界