真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JAVA泛型淺析

本文主要列舉了在使用Java泛型時應該注意的問題。Java泛型是Java5的一個重要特性,它和自動裝箱、變長參數(shù)等新特性一起,提升了Java代碼的健壯性和易用性,但SUN本身過分強調向前的兼容性,也引入了不少問題和麻煩。[@more@]

JAVA泛型和C++泛型的區(qū)別:

創(chuàng)新互聯(lián)長期為超過千家客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為黃巖企業(yè)提供專業(yè)的成都做網(wǎng)站、成都網(wǎng)站建設,黃巖網(wǎng)站改版等技術服務。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

Java的泛型被定義成擦除,而C++的泛型則是擴展;

對于C++模板,當參數(shù)類型為不同的類型時,生成的模板實例也是不同的類型,如:定義類模板

Template class A : public B;

當實例化模板時

A a;

A b;

這里的a和b是兩種不同的類型的實例;

Java則不是這樣的,如泛化List,分別用Integer和String來實例化,

List l;

List s;

通過反射機制看l和s的class,他們都是List!所有的參數(shù)類型都被編譯器擦除了!

這樣造成的結果是以往用C++模板可以實現(xiàn)某種契約(contract)的功能在Java中變得很另類了,舉一個例子:

C++代碼:

Template class A{

Private:

T x;

Public:

Void func(){

int ret = x.foo(12);

}

}

上面這段代碼在C++中經(jīng)常能夠看到,它暗中就設定了一個契約:凡是能否實例化這個模板的類型T,其必須具有一個公共的函數(shù)foo,這個函數(shù)返回整數(shù),并且接受一個整數(shù)做為參數(shù)。

Java的代碼:

public class A{

private E e;

public void func(){

int ret = e.foo(12); //編譯錯誤啊…

}

}

編譯器給出的錯誤原因是foo函數(shù)對于類型中E是未定義的??!造成這樣的問題的原因有兩個:

1、C++的編譯器直到模板被使用(實例化)的時候才去編譯模板,如果你不去使用模板C++編譯器不會編譯模板;而實例化的時候編譯器已經(jīng)能夠確定具體的參數(shù)類型,所以能夠檢測契約是否符合;Java的編譯器不是這樣工作的,所以它在編譯模板類型的時候不能夠確定E到底有沒有這個foo函數(shù);

2、類型擦除的結果;修改一下上面的程序,我們看看在func中到底能夠調用什么函數(shù),一看只能調用Object對象中的函數(shù)。所有的類型E都被擦除成Object了!

如果真的要想實現(xiàn)類似C++的契約,就必須確保參數(shù)類型E不被擦除成Object!需要如下修改代碼:

public class A{

private E e;

public void func(){

int ret = e.foo(12);

}

}

Class B{

Public int foo(int param){…};

}

這樣雖然可以實現(xiàn)我們期望的形式,但是約束的程度要比C++的強很多,C++中,只要任意類型,其具有一個符合契約的函數(shù),就可以實例化模板,而Java中,則要求所有的類型必須是給定類型的子類才可以實例化模板;

擦除的原則:

1、所有參數(shù)化容器類都被擦除成非參數(shù)化的(raw type);如List、List>都被擦除成List;

2、所有參數(shù)化數(shù)組都被擦除成非參數(shù)化的數(shù)組;如List[],被擦除成List[];

3、Raw type的容器類,被擦除成其自身,如List被擦除成List;

4、原生類型(int,String還有wrapper類)都擦除成他們的自身;

5、參數(shù)類型E,被擦除成Object;

6、所有約束參數(shù)如、都被擦除成E;

7、如果有多個約束,擦除成第一個,如,則擦除成Object;

例如:

泛化代碼:

                 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 = (擦除后的代碼和以前沒有泛型時候寫的代碼沒有任何區(qū)別!
再例如:
泛化代碼:

publicclass textReader{

privateTa;

public textReader(T b){

this.a = b;

}

publicT getA(){

returna;

}

publicstaticvoid main(String[] agrvs){

String in ="1234567890";

textReader test =new textReader(in);

String out = test.getA();

System.out.println(out);

}

      }
      擦除后(所有類型參數(shù)都被去掉,T被擦除成Object)就變成(注意紅色部分):

publicclass textReader{

privateObjecta;

public textReader(Object b){

this.a = b;

}

publicObject getA(){

returna;

}

publicstaticvoid main(String[] agrvs){

String in ="1234567890";

textReader test =new textReader (in);

String out = (String)test.getA();

System.out.println(out);

}

      }

擦除所帶來的問題:

1、靜態(tài)成員共享問題

        List ints = Arrays.asList(1,2,3);
      List strings = Arrays.asList("one","two");
      assert ints.getClass() == strings.getClass();

ints和strings兩個對象最終被擦除成具有相同類型的(List)的對象,于是這兩個對象共享List的靜態(tài)成員,于是就可以得出這樣的結論,所有泛化類型的靜態(tài)成員被其所有的實例化對象共享,因此也就要求所有靜態(tài)成員不能夠是泛化的!

class Foo {
private final T value;
private public T getValue() { return value; }
        public 2、過載(overload)沖突問題

函數(shù)過載的定義這樣的:在一個類的范圍內(nèi),如果兩個函數(shù)具有相同的函數(shù)名稱,不同的參數(shù)(返回值不考慮)就互相稱為過載函數(shù)??匆粋€例子:

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,這樣兩個函數(shù)重復定義,報錯;

Public static int foo1(List a){}

Public static String foo1(List s){} //沒有問題,編譯器不會報錯!

}

3、接口實現(xiàn)

一個類不能同時實現(xiàn)具有相同擦除效果的接口,例如:

class Foo implements Comparable, Comparable

繼承關系:

1、原始繼承:就是我們經(jīng)常提到的繼承關系,如ArrayListList的子類;

2、泛化繼承:

a)泛化后的ArrayList依舊是List的子類;其中T是參數(shù)化類型

b)如果類型T是類型B的子類,那么List不是List的子類

c)List是List的子類

d)List是List的子類

e)List是List的子類

f)List是List的子類

g)如果類型T是類型B的子類,那么T[]是B[]的子類

3、關于協(xié)變式(covariant)、不變式(invariant)和反變式(contravariant):

a)數(shù)組和擴展類(extends)的泛化是協(xié)變式,即如果類型T是類型B的子類,那么T[]是B[]的子類;List是List的子類

b)非擴展類泛型是不變式,即如果類型T是類型B的子類,那么List不是List的子類

c)Super類泛型是反變式,即如果B是T的超類,則List是List的子類

Get和Put原則:

當從一個泛化的結構中取數(shù)據(jù)的時候請使用extends通配,當往一個泛化的結構中放數(shù)據(jù)的時候請使用super通配;

當需要同時從一個泛化結構中讀取和寫入數(shù)據(jù)是,請不使用通配符號;

為什么會這樣,我們簡單的分析一下:假定類型T繼承自A和B,類型C和D又從T類型繼承,那么List中存放的只能是T類型或是C或是D類型,里面存放的類型都可以向上cast到T,所以從List中取東西編譯器能夠正確處理,只要映射到T就可以了(T是他們的父類)!往List放東西就不一樣的,原來里面放的是T/C還是D都被擦除成T,所以編譯器不知道原來到底存放的是什么類型,無法保證類型安全,所以這個操作被禁止!

List中存放的是A/B或是T,往List放T是允許的,因為T總是可以向上轉換成A或是B,但是從里面取東西就有問題了,編譯器還是不能夠確定List里面放的是什么類型,可能是A也可能是B。

具體化:

當一個類型能夠在運行時態(tài)被完整的表現(xiàn),我們就稱為其是可以具體化的,舉一個例子:

List就不是一個可以具體化的類型,因為它在運行時態(tài)被擦除成List!所以當處理一些需要運行時檢測類型的操作的時候(如instanceof)就要特別注意。

究竟哪些類型是可具體化的呢;

(1)、原始類型,如int;

(2)、非參數(shù)化的類和接口;

(3)、非限定的參數(shù)化類型,如List, Map

(4)、Raw類型,如 List,ArrayList等

(5)、所有由可具體化類型組成的數(shù)組,如Number[],List[]等

哪些是不可具體化的類型呢

(1)、類型變量,如T;

(2),參數(shù)化類型,如List

(3),有限定的參數(shù)化類型,如List;

具體哪些操作需要注意區(qū)分是否是具體化類型:


當前標題:JAVA泛型淺析
路徑分享:http://weahome.cn/article/podpoi.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部