麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 編程 > Java > 正文

Java 泛型詳解

2019-11-11 07:44:47
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

學(xué)習(xí)java的同學(xué)注意了!!! 學(xué)習(xí)過(guò)程中遇到什么問(wèn)題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號(hào)碼:183993990  我們一起學(xué)Java!

泛型概述

Java泛型(generics)是JDK 5中引入的一個(gè)新特性,允許在定義類和接口的時(shí)候使用類型參數(shù)(type parameter)。聲明的類型參數(shù)在使用時(shí)用具體的類型來(lái)替換。

優(yōu)缺點(diǎn)

從好的方面來(lái)說(shuō),泛型的引入可以解決之前的集合類框架在使用過(guò)程中通常會(huì)出現(xiàn)的運(yùn)行時(shí)刻類型錯(cuò)誤,因?yàn)榫幾g器可以在編譯時(shí)刻就發(fā)現(xiàn)很多明顯的錯(cuò)誤。而從不好的地方來(lái)說(shuō),為了保證與舊有版本的兼容性,Java泛型的實(shí)現(xiàn)上存在著一些不夠優(yōu)雅的地方。當(dāng)然這也是任何有歷史的編程語(yǔ)言所需要承擔(dān)的歷史包袱。后續(xù)的版本更新會(huì)為早期的設(shè)計(jì)缺陷所累。

舉例

List作為形式參數(shù),那么如果嘗試將一個(gè)List的對(duì)象作為實(shí)際參數(shù)傳進(jìn)去,卻發(fā)現(xiàn)無(wú)法通過(guò)編譯。雖然從直覺上來(lái)說(shuō),Object是String的父類,這種類型轉(zhuǎn)換應(yīng)該是合理的。但是實(shí)際上這會(huì)產(chǎn)生隱含的類型轉(zhuǎn)換問(wèn)題,因此編譯器直接就禁止這樣的行為。

類型擦除

正確理解泛型概念的首要前提是理解類型擦除(type erasure)。

Java中的泛型基本上都是在編譯器這個(gè)層次來(lái)實(shí)現(xiàn)的。

在生成的Java字節(jié)代碼中是不包含泛型中的類型信息的。使用泛型的時(shí)候加上的類型參數(shù),會(huì)被編譯器在編譯的時(shí)候去掉。這個(gè)過(guò)程就稱為類型擦除。

如在代碼中定義的List和List等類型,在編譯之后都會(huì)變成List。JVM看到的只是List,而由泛型附加的類型信息對(duì)JVM來(lái)說(shuō)是不可見的。Java編譯器會(huì)在編譯時(shí)盡可能的發(fā)現(xiàn)可能出錯(cuò)的地方,但是仍然無(wú)法避免在運(yùn)行時(shí)刻出現(xiàn)類型轉(zhuǎn)換異常的情況。類型擦除也是Java的泛型實(shí)現(xiàn)方式與C++模板機(jī)制實(shí)現(xiàn)方式之間的重要區(qū)別。

很多泛型的奇怪特性都與這個(gè)類型擦除的存在有關(guān)

1.泛型類并沒有自己獨(dú)有的Class類對(duì)象。比如并不存在List.class或是List.class,而只有List.class。

2.靜態(tài)變量是被泛型類的所有實(shí)例所共享的。對(duì)于聲明為MyClass的類,訪問(wèn)其中的靜態(tài)變量的方法仍然是 MyClass.myStaticVar。不管是通過(guò)new MyClass還是new MyClass創(chuàng)建的對(duì)象,都是共享一個(gè)靜態(tài)變量。

3.泛型的類型參數(shù)不能用在Java異常處理的catch語(yǔ)句中。因?yàn)楫惓L幚硎怯蒍VM在運(yùn)行時(shí)刻來(lái)進(jìn)行的。由于類型信息被擦除,JVM是無(wú)法區(qū)分兩個(gè)異常類型MyException和MyException的。對(duì)于JVM來(lái)說(shuō),它們都是 MyException類型的。也就無(wú)法執(zhí)行與異常對(duì)應(yīng)的catch語(yǔ)句。

類型擦除的過(guò)程

類型擦除的基本過(guò)程也比較簡(jiǎn)單,首先是找到用來(lái)替換類型參數(shù)的具體類。這個(gè)具體類一般是Object。如果指定了類型參數(shù)的上界的話,則使用這個(gè)上界。把代碼中的類型參數(shù)都替換成具體的類。同時(shí)去掉出現(xiàn)的類型聲明,即去掉<>的內(nèi)容。比如T get()方法聲明就變成了Object get();List就變成了List。接下來(lái)就可能需要生成一些橋接方法(bridge method)。這是由于擦除了類型之后的類可能缺少某些必須的方法。

實(shí)例分析

了解了類型擦除機(jī)制之后,就會(huì)明白編譯器承擔(dān)了全部的類型檢查工作。編譯器禁止某些泛型的使用方式,正是為了確保類型的安全性。以上面提到的List和List為例來(lái)具體分析:

public void inspect(List<Object> list) {        for (Object obj : list) {                System.out.PRintln(obj);        }        list.add(1); //這個(gè)操作在當(dāng)前方法的上下文是合法的。 }public void test() {        List<String> strs = new ArrayList<String>();        inspect(strs); //編譯錯(cuò)誤 }

這段代碼中,inspect方法接受List作為參數(shù),當(dāng)在test方法中試圖傳入List的時(shí)候,會(huì)出現(xiàn)編譯錯(cuò)誤。假設(shè)這樣的做法是允許的,那么在inspect方法就可以通過(guò)list.add(1)來(lái)向集合中添加一個(gè)數(shù)字。這樣在test方法看來(lái),其聲明為L(zhǎng)ist的集合中卻被添加了一個(gè)Integer類型的對(duì)象。這顯然是違反類型安全的原則的,在某個(gè)時(shí)候肯定會(huì)拋出ClassCastException。因此,編譯器禁止這樣的行為。編譯器會(huì)盡可能的檢查可能存在的類型安全問(wèn)題。對(duì)于確定是違反相關(guān)原則的地方,會(huì)給出編譯錯(cuò)誤。當(dāng)編譯器無(wú)法判斷類型的使用是否正確的時(shí)候,會(huì)給出警告信息。

泛型類

容器類應(yīng)該算得上最具重用性的類庫(kù)之一。先來(lái)看一個(gè)沒有泛型的情況下的容器類如何定義:

public class Container {    private String key;    private String value;    public Container(String k, String v) {        key = k;        value = v;    }    public String getKey() {        return key;    }    public void setKey(String key) {        this.key = key;    }    public String getValue() {        return value;    }    public void setValue(String value) {        this.value = value;    }}

Container類保存了一對(duì)key-value鍵值對(duì),但是類型是定死的,也就說(shuō)如果我想要?jiǎng)?chuàng)建一個(gè)鍵值對(duì)是String-Integer類型的,當(dāng)前這個(gè)Container是做不到的,必須再自定義。那么這明顯重用性就非常低。

當(dāng)然,我可以用Object來(lái)代替String,并且在Java SE5之前,我們也只能這么做,由于Object是所有類型的基類,所以可以直接轉(zhuǎn)型。但是這樣靈活性還是不夠,因?yàn)檫€是指定類型了,只不過(guò)這次指定的類型層級(jí)更高而已,有沒有可能不指定類型?有沒有可能在運(yùn)行時(shí)才知道具體的類型是什么?

所以,就出現(xiàn)了泛型。

public class Container<K, V> {    private K key;    private V value;    public Container(K k, V v) {        key = k;        value = v;    }    public K getKey() {        return key;    }    public void setKey(K key) {        this.key = key;    }    public V getValue() {        return value;    }    public void setValue(V value) {        this.value = value;    }}

在編譯期,是無(wú)法知道K和V具體是什么類型,只有在運(yùn)行時(shí)才會(huì)真正根據(jù)類型來(lái)構(gòu)造和分配內(nèi)存。可以看一下現(xiàn)在Container類對(duì)于不同類型的支持情況:

public class Main {    public static void main(String[] args) {        Container<String, String> c1 = new Container<String, String>("name", "findingsea");        Container<String, Integer> c2 = new Container<String, Integer>("age", 24);        Container<Double, Double> c3 = new Container<Double, Double>(1.1, 2.2);        System.out.println(c1.getKey() + " : " + c1.getValue());        System.out.println(c2.getKey() + " : " + c2.getValue());        System.out.println(c3.getKey() + " : " + c3.getValue());    }}
輸出:name : findingseaage : 241.1 : 2.2

泛型接口

在泛型接口中,生成器是一個(gè)很好的理解,看如下的生成器接口定義:

public interface Generator<T> {    public T next();}然后定義一個(gè)生成器類來(lái)實(shí)現(xiàn)這個(gè)接口:public class FruitGenerator implements Generator<String> {    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};    @Override    public String next() {        Random rand = new Random();        return fruits[rand.nextInt(3)];    }}調(diào)用:public class Main {    public static void main(String[] args) {        FruitGenerator generator = new FruitGenerator();        System.out.println(generator.next());        System.out.println(generator.next());        System.out.println(generator.next());        System.out.println(generator.next());    }}輸出:BananaBananaPearBanana

泛型方法

一個(gè)基本的原則是:無(wú)論何時(shí),只要你能做到,你就應(yīng)該盡量使用泛型方法。也就是說(shuō),如果使用泛型方法可以取代將整個(gè)類泛化,那么應(yīng)該有限采用泛型方法。下面來(lái)看一個(gè)簡(jiǎn)單的泛型方法的定義:

public class Main {    public static <T> void out(T t) {        System.out.println(t);    }    public static void main(String[] args) {        out("findingsea");        out(123);        out(11.11);        out(true);    }}

可以看到方法的參數(shù)徹底泛化了,這個(gè)過(guò)程涉及到編譯器的類型推導(dǎo)和自動(dòng)打包,也就說(shuō)原來(lái)需要我們自己對(duì)類型進(jìn)行的判斷和處理,現(xiàn)在編譯器幫我們做了。這樣在定義方法的時(shí)候不必考慮以后到底需要處理哪些類型的參數(shù),大大增加了編程的靈活性。

再看一個(gè)泛型方法和可變參數(shù)的例子:

public class Main {    public static <T> void out(T... args) {        for (T t : args) {            System.out.println(t);        }    }    public static void main(String[] args) {        out("findingsea", 123, 11.11, true);    }}

通配符與上下界

在使用泛型類的時(shí)候,既可以指定一個(gè)具體的類型,如List就聲明了具體的類型是String;也可以用通配符?來(lái)表示未知類型,如List

類型系統(tǒng)

在Java中,大家比較熟悉的是通過(guò)繼承機(jī)制而產(chǎn)生的類型體系結(jié)構(gòu)。比如String繼承自O(shè)bject。根據(jù)Liskov替換原則,子類是可以替換父類的。當(dāng)需要Object類的引用的時(shí)候,如果傳入一個(gè)String對(duì)象是沒有任何問(wèn)題的。但是反過(guò)來(lái)的話,即用父類的引用替換子類引用的時(shí)候,就需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換。編譯器并不能保證運(yùn)行時(shí)刻這種轉(zhuǎn)換一定是合法的。這種自動(dòng)的子類替換父類的類型轉(zhuǎn)換機(jī)制,對(duì)于數(shù)組也是適用的。 String[]可以替換Object[]。但是泛型的引入,對(duì)于這個(gè)類型系統(tǒng)產(chǎn)生了一定的影響。正如前面提到的List是不能替換掉List的。

引入泛型之后的類型系統(tǒng)增加了兩個(gè)維度:

一個(gè)是類型參數(shù)自身的繼承體系結(jié)構(gòu),另外一個(gè)是泛型類或接口自身的繼承體系結(jié)構(gòu)。第一個(gè)指的是對(duì)于 List和List這樣的情況,類型參數(shù)String是繼承自O(shè)bject的。而第二種指的是 List接口繼承自Collection接口。對(duì)于這個(gè)類型系統(tǒng),有如下的一些規(guī)則:

相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。

即List是Collection 的子類型,List可以替換Collection。這種情況也適用于帶有上下界的類型聲明。

當(dāng)泛型類的類型聲明中使用了通配符的時(shí)候, 其子類型可以在兩個(gè)維度上分別展開。如對(duì)Collection

泛型的命名規(guī)范

為了更好地去理解泛型,我們也需要去理解java泛型的命名規(guī)范。為了與java關(guān)鍵字區(qū)別開來(lái),java泛型參數(shù)只是使用一個(gè)大寫字母來(lái)定義。各種常用泛型參數(shù)的意義如下:

E — Element,常用在java Collection里,如:List,Iterator,SetK,V — Key,Value,代表Map的鍵值對(duì)N — Number,數(shù)字T — Type,類型,如String,Integer等等S,U,V etc. – 2nd, 3rd, 4th 類型,和T的用法一樣

學(xué)習(xí)Java的同學(xué)注意了!!! 學(xué)習(xí)過(guò)程中遇到什么問(wèn)題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號(hào)碼:183993990  我們一起學(xué)Java!


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 国产日产精品久久久久快鸭 | 欧美性生活网站 | 91av久久 | 成人综合一区二区 | 99视频有精品 | 成人午夜亚洲 | 综合网天天色 | 精品国产一区二区三区蜜殿 | 成人福利视频在线 | 亚洲第一页中文字幕 | videos韩国 | 久久网国产| av在线免费看网址 | 羞羞视频在线免费 | 免费观看的毛片手机视频 | 中文字幕亚洲一区二区三区 | 国产成人自拍小视频 | 欧美一区二区网站 | 午夜精品福利在线观看 | 在线看一级片 | 国产无限资源在线观看 | 国产免费久久久 | 美女黄色毛片免费看 | 欧美成人性色区 | 欧美一级黄色免费看 | 中文字幕电影免费播放 | 国产99页 | 亚洲天堂中文字幕在线观看 | 高清视频一区二区 | 国产正在播放 | 国产精品久久久久久久成人午夜 | 黄色大片免费网站 | 亚洲第一成人在线视频 | 美女一级视频 | 亚洲精品成人18久久久久 | 毛片大全 | h视频免费在线观看 | 91超在线| 久久久久久久久久久国产精品 | 毛片在线视频免费观看 | 国产精品一区二区三区在线 |