JAVA泛型淺析
JAVA泛型和C++泛型的區別:
Java的泛型被定義成擦除,而C++的泛型則是擴充套件;
對於C++模板,當引數型別為不同的型別時,生成的模板例項也是不同的型別,如:定義類别範本
Template
當例項化模板時
A
A<:string> b;
這裡的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、 如果有多個約束,擦除成第一個,如
例如:
泛化程式碼:
Listwords = new ArrayList ();
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 = ((String)words.get(0))+((String)words.get(1))
擦除後的程式碼和以前沒有泛型時候寫的程式碼沒有任何區別!
再例如:
泛化程式碼:
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 static List<T> values = new ArrayList<T>(); //非法
public T getValue() { return value; }
public static List<Object> values = new ArrayList<Object>() // ok
}
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
Public static int foo1(List
Public static int foo1(List
Public static String foo1(List
}
3、 介面實現
一個類不能同時實現具有相同擦除效果的介面,例如:
class Foo implements Comparable
Comparable
繼承關係:
1、 原始繼承:就是我們經常提到的繼承關係,如ArrayList是List的子類;
2、 泛化繼承:
a) 泛化後的ArrayList
b) 如果型別T是型別B的子類,那麼List
c) List
d) List
e) List
f) List是List super T>的子類
g) 如果型別T是型別B的子類,那麼T[]是B[]的子類
3、 關於協變式(covariant)、不變式(invariant)和反變式(contravariant):
a) 陣列和擴充套件類(extends)的泛化是協變式,即如果型別T是型別B的子類,那麼T[]是B[]的子類;List
b) 非擴充套件類泛型是不變式,即如果型別T是型別B的子類,那麼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
究竟哪些型別是可具體化的呢;
(1)、原始型別,如int;
(2)、非引數化的類和介面;
(3)、非限定的引數化型別,如List>, Map,?>
(4)、Raw型別,如 List,ArrayList等
(5)、所有由可具體化型別組成的陣列,如Number[],List>[]等
哪些是不可具體化的型別呢
(1)、型別變數,如T;
(2),引數化型別,如List
(3),有限定的引數化型別,如List extends Number>;
具體哪些操作需要注意區分是否是具體化型別:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/71047/viewspace-899480/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java泛型應用淺析Java泛型
- 淺談java泛型Java泛型
- java多型性淺析Java多型
- Java基礎之淺談泛型Java泛型
- 【java】【泛型】泛型geneticJava泛型
- java泛型之泛型方法。Java泛型
- Java泛型知識點:泛型類、泛型介面和泛型方法Java泛型
- java泛型之泛型陣列。Java泛型陣列
- Java 泛型Java泛型
- Java泛型Java泛型
- 淺談C#泛型C#泛型
- 淺析Java反射--JavaJava反射
- 淺析Java NIOJava
- 淺析JAVA反射Java反射
- Java 泛型原理Java泛型
- Java+泛型Java泛型
- java泛型一二Java泛型
- Java(7)泛型Java泛型
- Java-泛型Java泛型
- java泛型剖析Java泛型
- JAVA泛型類Java泛型
- Oracle Xmltype型別淺析OracleXML型別
- Timestamp型別淺析型別
- Java快取淺析Java快取
- 淺析Java常量池Java
- 淺析Java斷言Java
- Java偏向鎖淺析Java
- Java™ 教程(泛型原始型別)Java泛型型別
- Java泛型與型別擦除Java泛型型別
- java 基礎 泛型Java泛型
- Java:Collection集合、泛型Java泛型
- Java泛型知識Java泛型
- Java泛型複習Java泛型
- 認識Java泛型Java泛型
- Java 泛型詳解Java泛型
- Java泛型筆記Java泛型筆記
- Java基礎-泛型Java泛型
- JAVA泛型入門Java泛型