Java泛型食用筆記(一) -- 基本介紹

Java泛型食用筆記(一) -- 基本介紹


JDK5 將泛型引入,這是 Java 走向類型安全的一大步,然而,在學(xué)習(xí)和使用泛型的過程中,幾乎都會(huì)遇到令人沮喪的問題。本系列試圖將 Java 的泛型解釋清楚,幫助開發(fā)者在開發(fā)中正確的使用泛型。

1. 沒有泛型的糟糕世界

引入泛型的原因不少,其中一個(gè)重要原因就是為了容器類的優(yōu)雅實(shí)現(xiàn)。

在沒有泛型前,當(dāng)需要實(shí)現(xiàn)一個(gè)可以存儲(chǔ)任何類型對象的 Hashtable 時(shí),會(huì)定義如下接口:

public class Hashtable {
...
    public Object put(Object key, Object value) {...}
    public Object get(Object key) {...}
...
}

當(dāng)你向 Hashtable 中插入對象時(shí),比如一個(gè) String 對象,Hashtable 會(huì)把類型信息抹去,退化成 Object 對象進(jìn)行存儲(chǔ)。此時(shí)只有你的腦袋中存有類型信息,當(dāng)你需要從 Hashtable 獲取元素時(shí),必須對其進(jìn)行強(qiáng)制類型轉(zhuǎn)換才能獲得你所需的 String 對象。

...
    Hashtable h = new Hashtable();
    h.put("string", "value");
    String s = (String)h.get("string");
...

這樣的代碼直覺上就不太讓人放心。每次強(qiáng)制類型轉(zhuǎn)換,不僅增加冗余的代碼,同時(shí)也是一次忽略編譯器進(jìn)行靜態(tài)類型檢查的過程,如果轉(zhuǎn)換過程中出現(xiàn)錯(cuò)誤,就會(huì)拋出 ClassCastException 異常,除非你能確保轉(zhuǎn)換無誤,否則你需要更過的代碼來進(jìn)行錯(cuò)誤處理。在泛型出來之前,這幾乎是無解題。

2. 泛型的引入

Java 泛型的核心就是告訴編譯器想使用什么類型,然后編譯器幫你處理一切細(xì)節(jié)。泛型也是一種參數(shù),只不過參數(shù)傳入的是類型。引入泛型可以讓一些實(shí)現(xiàn)更優(yōu)雅。

使用泛型重新定義 Hashtable 及其接口:

public class Hashtable<K, V> {
...
    public V put(K key, V value) {...}
    public V get(K key) {...}
...
}

其中 <K, V> 即為類型參數(shù),其作用域?yàn)轭惗x的主體部分(除靜態(tài)成員)。當(dāng)使用泛型類時(shí),你要將你所需使用的類型傳入。

...
    Hashtable<String, String> h = new Hashtable<>();
    h.put("string", "value");
    String s = h.get("string");
...

使用泛型后的 Hashtable 類更加簡潔。我們來看下泛型帶來了什么:

  • 將類型信息告訴編譯器
  • 放入元素時(shí)編譯器進(jìn)行類型檢查
  • 取出元素自動(dòng)轉(zhuǎn)換類型

編譯器在編譯期間就會(huì)進(jìn)行類型檢查,防止在運(yùn)行期間出現(xiàn)類型錯(cuò)誤。

3. 泛型接口

泛型也可用于接口,比如需要寫一個(gè)生成器:

interface Gernerator<T> {
    T next();
}

class DrinkGernerator implements Gernerator<Drink> {
    private Drink[] drinks = {new Water(){}, new Coke(){}, new Coffee(){}};
    private Random seed = new Random();

    @Override
    public Drink next() {
        return drinks[seed.nextInt(3)];
    }
}

abstract class Drink {
    public abstract String name();
}

class Water extends Drink {
    @Override
    public String name() {
        return "Water";
    }
}

class Coke extends Drink {
    @Override
    public String name() {
        return "Coke";
    }
}

class Coffee extends Drink {
    @Override
    public String name() {
        return "Coffee";
    }
}

看上去是不是特別眼熟,容器的迭代器 Iterator<E> 就是一個(gè)典型的生成器接口。

4. 泛型方法

之前據(jù)的所有例子都是作用與整個(gè)類的,泛型也可以僅僅應(yīng)用在方法上,也就是接下來要介紹的泛型方法。原則上,能夠使用泛型方法的時(shí)候就盡量避免使用泛型類,這會(huì)使你的代碼看上去更加清楚。另外,如果 static 方法需要使用泛型,只能使用泛型方法。

泛型方法的使用方法就是將泛型參數(shù)置于返回值之前:

public class GernericMethod {
    public static <T> void printClassName(T t) {
        System.out.println(t.getClass().getName());
    }

    public static void main(String[] args) {
        printClassName("string");
        printClassName(1);
        printClassName(2.1);
    }
}

output:

java.lang.String
java.lang.Integer
java.lang.Double

看上去 printClassName 方法就像無限重載過,無論傳入什么類型的參數(shù),都可以順利執(zhí)行。這是因?yàn)榉盒头椒ㄔ谑褂脮r(shí),編譯器會(huì)進(jìn)行參數(shù)推斷,幫助我們找到具體的類型。

小結(jié)

泛型可以用于泛型類,泛型接口,泛型方法,并都有各自的使用場景。引入泛型后,可以避免很多蹩腳的類型轉(zhuǎn)換等操作,讓代碼實(shí)現(xiàn)更為優(yōu)雅,看上去一切都很美好。接下來我們將探討 Java 泛型的實(shí)現(xiàn)原理及帶來的問題,以幫助我們更好的理解和使用 Java 泛型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,853評(píng)論 18 399
  • 目錄 大概是飛機(jī)上十二個(gè)小時(shí)的祈禱起了作用,蘇錯(cuò)回家沒看到親娘。父親說母親很難得地跟幾個(gè)朋友出門旅游去了。說這話的...
    書咄咄閱讀 702評(píng)論 3 4
  • 換季啦~換季啦~衣柜里總?cè)奔路?( ?? )? 鞋架上總?cè)彪p鞋子( ?? ??? )噢~還缺口紅、蜜粉……缺各種...
    就叫yi顆小黃豆閱讀 430評(píng)論 3 1
  • 周一我們閱覽了有關(guān)稻盛先生的視頻,現(xiàn)場很多著名的當(dāng)代企業(yè)家也甚為膜拜這位神一般的老人。每個(gè)人的成功,一定有...
    書_贏閱讀 512評(píng)論 0 1
  • 每日推薦: 每日一歌――薛之謙《王子公主》 每日一影――郭在容《我的早更女友》 每日一詩――白居易《暮江吟》 ...
    薩拉芯雪閱讀 261評(píng)論 0 0

友情鏈接更多精彩內(nèi)容