JAVA泛型和C++泛型的區別:
Java的泛型被定義成擦除,而C++的泛型則是擴展;
對于C++模板,當參數類型為不同的類型時,生成的模板實例也是不同的類型,如:定義類模板
Template
當實例化模板時
A
A
這里的a和b是兩種不同的類型的實例;
Java則不是這樣的,如泛化List
List
List
通過反射機制看l和s的class,他們都是List!所有的參數類型都被編譯器擦除了!
這樣造成的結果是以往用C++模板可以實現某種契約(contract )的功能在Java中變得很另類了,舉一個例子:
C++代碼:
Template
Private:
T x;
Public:
Void func(){
int ret = x.foo(12);
}
}
上面這段代碼在C++中經常能夠看到,它暗中就設定了一個契約:凡是能否實例化這個模板的類型T,其必須具有一個公共的函數foo,這個函數返回整數,并且接受一個整數做為參數。
Java的代碼:
public class A
private E e;
public void func(){
int ret = e.foo(12); //編譯錯誤啊…
}
}
編譯器給出的錯誤原因是foo函數對于類型中E是未定義的??!造成這樣的問題的原因有兩個:
1、 C++的編譯器直到模板被使用(實例化)的時候才去編譯模板,如果你不去使用模板C++編譯器不會編譯模板;而實例化的時候編譯器已經能夠確定具體的參數類型,所以能夠檢測契約是否符合;Java的編譯器不是這樣工作的,所以它在編譯模板類型的時候不能夠確定E到底有沒有這個foo函數;
2、 類型擦除的結果;修改一下上面的程序,我們看看在func中到底能夠調用什么函數,一看只能調用Object對象中的函數。所有的類型E都被擦除成Object了!
如果真的要想實現類似C++的契約,就必須確保參數類型E不被擦除成Object!需要如下修改代碼:
public class A
private E e;
public void func(){
int ret = e.foo(12);
}
}
Class B{
Public int foo(int param){…};
}
這樣雖然可以實現我們期望的形式,但是約束的程度要比C++的強很多,C++中,只要任意類型,其具有一個符合契約的函數,就可以實例化模板,而Java中,則要求所有的類型必須是給定類型的子類才可以實例化模板;
擦除的原則:
1、 所有參數化容器類都被擦除成非參數化的(raw type);如List>
都被擦除成List;
2、 所有參數化數組都被擦除成非參數化的數組;如List
3、 Raw type的容器類,被擦除成其自身,如List 被擦除成List;
4、 原生類型(int,String還有wrapper類)都擦除成他們的自身;
5、 參數類型E,被擦除成Object;
6、 所有約束參數如 Extends E>、
7、 如果有多個約束,擦除成第一個,如
例如:
泛化代碼:
List words.add("Hello ");
words.add("world!"); String s = words.get(0)+words.get(1);擦除后就變成了:
List words = new ArrayList();
words.add("Hello "); words.add("world!"); String s = ( 擦除后的代碼和以前沒有泛型時候寫的代碼沒有任何區別!再例如:
泛化代碼:
public class textReader<T>{
private T a;
public textReader(T b){
this.a = b;
}
public T getA(){
return a;
}
public static void main(String[] agrvs){
String in = "1234567890";
textReader
String out = test.getA();
System.out.println(out);
}
}擦除后(所有類型參數都被去掉,T被擦除成Object)就變成(注意紅色部分):
public class textReader{
private Object a;
public textReader(Object b){
this.a = b;
}
public Object getA(){
return a;
}
public static void main(String[] agrvs){
String in = "1234567890";
textReader test = new textReader (in);
String out = (String)test.getA();
System.out.println(out);
}
}
擦除所帶來的問題:
1、 靜態成員共享問題
Listints = Arrays.asList(1,2,3);
List strings = Arrays.asList("one","two"); assert ints.getClass() == strings.getClass();ints和strings兩個對象最終被擦除成具有相同類型的(List)的對象,于是這兩個對象共享List的靜態成員,于是就可以得出這樣的結論,所有泛化類型的靜態成員被其所有的實例化對象共享,因此也就要求所有靜態成員不能夠是泛化的!
class Foo { private final T value; private public T getValue() { return value; } public 2、 過載(overload)沖突問題函數過載的定義這樣的:在一個類的范圍內,如果兩個函數具有相同的函數名稱,不同的參數(返回值不考慮)就互相稱為過載函數??匆粋€例子:
Class A{
Public int foo(int a){};
Public int foo(float f){}; // 是過載,編譯沒有問題
Public int foo(int a){};
Public float foo(int f){}; // 報錯
Public static int foo1(List a){}
Public static int foo1(List s){} // 編譯有錯誤,因為所有的List 都被擦除成List,這樣兩個函數重復定義,報錯;
Public static int foo1(List a){}
Public static String foo1(List s){} // 沒有問題,編譯器不會報錯!
}
3、接口實現
一個類不能同時實現具有相同擦除效果的接口,例如:
class Foo implements Comparable , Comparable
繼承關系:
1、 原始繼承:就是我們經常提到的繼承關系,如ArrayList是List的子類;
2、 泛化繼承:
a) 泛化后的ArrayList 依舊是List 的子類;其中T是參數化類型
b) 如果類型T是類型B的子類,那么List 不是List的子類
c) List 是List extends T>的子類
d) List 是List extends B>的子類
e) List 是List super T>的子類
f) List是List super T>的子類
g) 如果類型T是類型B的子類,那么T[]是B[]的子類
3、 關于協變式(covariant)、不變式(invariant)和反變式(contravariant):
a) 數組和擴展類(extends)的泛化是協變式,即如果類型T是類型B的子類,那么T[]是B[]的子類;List 是List extends B>的子類
b) 非擴展類泛型是不變式,即如果類型T是類型B的子類,那么List 不是List的子類
c) Super類泛型是反變式,即如果B是T的超類,則List 是List super T>的子類
Get和Put原則:
當從一個泛化的結構中取數據的時候請使用extends通配,當往一個泛化的結構中放數據的時候請使用super通配;
當需要同時從一個泛化結構中讀取和寫入數據是,請不使用通配符號;
為什么會這樣,我們簡單的分析一下:假定類型T繼承自A和B,類型C和D又從T類型繼承,那么List extends T>中存放的只能是T類型或是C或是D類型,里面存放的類型都可以向上cast到T,所以從List extends T>中取東西編譯器能夠正確處理,只要映射到T就可以了(T是他們的父類)!往List extends T>放東西就不一樣的,原來里面放的是T/C還是D都被擦除成T,所以編譯器不知道原來到底存放的是什么類型,無法保證類型安全,所以這個操作被禁止!
List super T>中存放的是A/B或是T,往List super T>放T是允許的,因為T總是可以向上轉換成A或是B,但是從里面取東西就有問題了,編譯器還是不能夠確定List里面放的是什么類型,可能是A也可能是B。
具體化:
當一個類型能夠在運行時態被完整的表現,我們就稱為其是可以具體化的,舉一個例子:
List 就不是一個可以具體化的類型,因為它在運行時態被擦除成List!所以當處理一些需要運行時檢測類型的操作的時候(如instanceof)就要特別注意。
究竟哪些類型是可具體化的呢;
(1)、原始類型,如int;
(2)、非參數化的類和接口;
(3)、非限定的參數化類型,如List>, Map,?>
(4)、Raw類型,如 List,ArrayList等
(5)、所有由可具體化類型組成的數組,如Number[],List>[]等
哪些是不可具體化的類型呢
(1)、類型變量,如T;
(2),參數化類型,如List 等
(3),有限定的參數化類型,如List extends Number>;
具體哪些操作需要注意區分是否是具體化類型:
向AI問一下細節
推薦閱讀:
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。