Java 泛型示例 - 泛型方法版扩,類,接口

Java Genrics 是 Java 5 中引入的最重要的功能之一。

如果您一直在使用Java Collections并使用版本 5 或更高版本,那么我確定您已經(jīng)使用過它艰亮。

Java 中具有集合類的泛型非常容易,但是它提供了比僅創(chuàng)建集合類型更多的功能挣郭。

我們將在本文中嘗試學習泛型的功能迄埃。如果我們使用專業(yè)術(shù)語,對泛型的理解有時會變得混亂兑障,因此侄非,我將盡量保持其簡單易懂。

1. Java 中的泛型

Java 5 中添加了泛型流译,以提供編譯時類型檢查逞怨,并消除了ClassCastException使用集合類時常見的風險。整個收集框架都進行了重寫福澡,以使用泛型進行類型安全叠赦。讓我們看看泛型如何幫助我們安全地使用集合類。

List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); 

for(Object obj : list){

    String str=(String) obj; 
}

上面的代碼可以很好地編譯竞漾,但是在運行時會引發(fā)ClassCastException眯搭,因為我們試圖將列表中的對象強制轉(zhuǎn)換為String窥翩,而其中一個元素是Integer類型业岁。在Java 5之后,我們使用如下收集類寇蚊。

List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>();
list1.add("abc");
//list1.add(new Integer(5)); //編譯錯誤

for(String str : list1){
     //no type casting needed, avoids ClassCastException
}

請注意笔时,在創(chuàng)建列表時,我們已指定列表中元素的類型為String仗岸。因此允耿,如果我們嘗試在列表中添加任何其他類型的對象,則該程序?qū)⒁l(fā)編譯時錯誤扒怖。還要注意较锡,在循環(huán)中中,我們不需要列表中元素的類型轉(zhuǎn)換盗痒,因此在運行時刪除了ClassCastException蚂蕴。

2. Java通用類

我們可以使用泛型類型定義自己的類低散。泛型類型是通過類型進行參數(shù)化的類或接口。我們使用尖括號(<>)來指定類型參數(shù)骡楼。

為了了解其好處熔号,我們假設有一個簡單的類:

package com.journaldev.generics;
public class GenericsTypeOld {
    private Object t;
    public Object get() {
        return t;
    }
    public void set(Object t) {
        this.t = t;
    }
        public static void main(String args[]){
        GenericsTypeOld type = new GenericsTypeOld();
        type.set("Pankaj");
         String str = (String) type.get(); //type casting, error prone and can cause ClassCastException
    }
}

請注意,在使用此類時鸟整,我們必須使用類型轉(zhuǎn)換引镊,并且它可以在運行時產(chǎn)生ClassCastException。現(xiàn)在篮条,我們將使用Java通用類替換如下所示的相同類弟头。

package com.journaldev.generics;
public class GenericsType<T> {
    private T t;
    public T get(){
        return this.t;
    }
    public void set(T t1){
        this.t=t1;
    }
    public static void main(String args[]){
        GenericsType<String> type = new GenericsType<>();
        type.set("Pankaj"); //valid
        GenericsType type1 = new GenericsType(); //raw type
        type1.set("Pankaj"); //valid
        type1.set(10); //valid and autoboxing support
    }
}

注意main方法中GenericsType類的使用。我們不需要進行類型轉(zhuǎn)換涉茧,并且可以在運行時刪除ClassCastException亮瓷。如果我們在創(chuàng)建時未提供類型,則編譯器將發(fā)出警告降瞳,“ GenericsType是原始類型嘱支。

泛型類型GenericsType 的引用應參數(shù)化”。當我們不提供類型時挣饥,該類型就變成了類型Object除师,因此它允許String和Integer對象。但是扔枫,我們應始終嘗試避免這種情況汛聚,因為在處理可能產(chǎn)生運行時錯誤的原始類型時,我們必須使用類型轉(zhuǎn)換短荐。

還要注意倚舀,它支持Java自動裝箱。

3. Java通用接口

Comparable接口是接口中泛型的一個很好的例子忍宋,它寫為:

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}

以類似的方式痕貌,我們可以在Java中創(chuàng)建通用接口。我們也可以像Map界面具有多個類型參數(shù)糠排。同樣舵稠,我們也可以為參數(shù)化類型提供參數(shù)化值,例如new HashMap<String, List<String>>();有效入宦。

4. Java通用類型

Java通用類型命名約定可以幫助我們輕松理解代碼哺徊,并且具有命名約定是Java編程語言的最佳實踐之一。因此乾闰,泛型也帶有自己的命名約定落追。通常,類型參數(shù)名稱是單個大寫字母涯肩,以可以實現(xiàn)與Java變量區(qū)分開轿钠。最常用的類型參數(shù)名稱為:

  • E –元素由Java Collections Framework廣泛使用雹熬,例如ArrayList,Set等
  • K –鍵(在Map中使用)
  • N –數(shù)字
  • T –類型
  • V –值(在Map中使用)
  • S谣膳,U竿报,V等–第二,第三继谚,第四類型

5. Java通用方法

有時我們不希望整個類都被參數(shù)化烈菌,在這種情況下,我們可以創(chuàng)建java泛型方法花履。由于構(gòu)造函數(shù)是一種特殊的方法芽世,因此我們也可以在構(gòu)造函數(shù)中使用泛型類型。

這是一個顯示Java泛型方法示例的類诡壁。

package com.journaldev.generics;
public class GenericsMethods {
    //Java Generic Method
    public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){
        return g1.get().equals(g2.get());
    }
    public static void main(String args[]){
        GenericsType<String> g1 = newGenericsType<>();
        g1.set("Pankaj");
        GenericsType<String> g2 = new GenericsType<>();
        g2.set("Pankaj");
        boolean isEqual = GenericsMethods.<String>isEqual(g1, g2);
        //above statement can be written simply as
        isEqual = GenericsMethods.isEqual(g1, g2);
        //This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
        //Compiler will infer the type that is needed
    }
}

注意的isEqual方法簽名顯示了在方法中使用泛型類型的語法济瓢。另外,請注意如何在我們的Java的程序中使用這些方法妹卿。我們可以在調(diào)用這些方法時指定類型旺矾,也可以像普通方法一樣調(diào)用它們。Java編譯器足夠聰明夺克,可以確定要使用的變量的類型箕宙,這種功能稱為類型變量。

6. Java泛型綁定類型參數(shù)

假設我們要限制可以在參數(shù)化類型中使用的對象的類型铺纽,例如在比較兩個對象的方法中柬帕,并且我們要確保接受的對象是可比較的。要聲明一個有界的類型參數(shù)狡门,請列出類型參數(shù)的名稱陷寝,然后列出擴展關(guān)鍵字,再加上其上限其馏,以下下面的方法凤跑。

public static <T extends Comparable<T>> int compare(T t1, T t2){
        return t1.compareTo(t2);
}

這些方法的調(diào)用與無界方法類似,不同之處在于尝偎,如果我們嘗試使用任何非Comparable的類饶火,則引發(fā)編譯時錯誤鹏控。

綁定類型參數(shù)可以與方法以及類和接口一起使用致扯。

Java泛型也支持多個范圍,即当辐。在這種情況下抖僵,A可以是接口或類。如果A是類缘揪,則B和C應該是接口耍群。在多個范圍內(nèi)义桂,我們不能有多個類。

7. Java泛型和繼承

我們知道蹈垢,如果A是B的子類慷吊,則Java繼承允許我們將變量A分配給另一個變量B。因此曹抬,我們可能認為可以將A的任何泛型類型分配給B的泛型類型溉瓶,但事實并非如此。讓我們用一個簡單的程序看看谤民。

package com.journaldev.generics;
public class GenericsInheritance {
    public static void main(String[] args) {
        String str = "abc";
        Object obj = new Object();
        obj=str; // works because String is-a Object, inheritance in java
        MyClass<String> myClass1 = new MyClass<String>();
        MyClass<Object> myClass2 = new MyClass<Object>();
        //myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object>
        obj = myClass1; // MyClass<T> parent is Object
    }
    public static class MyClass<T>{}
}

我們永久將MyClass 變量分配給MyClass 變量堰酿,因為它們不相關(guān),實際上MyClass 的父對象是Object张足。

8. Java通用類和子類型

我們可以通過擴展或?qū)崿F(xiàn)來泛型一個通用類或接口触创。一個類或接口的類型參數(shù)與另一類或接口的類型參數(shù)之間的關(guān)系由extend和實現(xiàn)子句確定。

例如为牍,ArrayList 實現(xiàn)了擴展Collection 的List 哼绑,因此ArrayList 是List 的子類型,而List 是Collection 的子類型碉咆。

只要不更改type參數(shù)凌那,子類型關(guān)系就會保留,下面顯示了多個type參數(shù)的示例吟逝。

interface MyList<E,T> extends List<E>{
}

List 的子類型可以是MyList 帽蝶,MyList 等。

9. Java通用通配符

問號(块攒?)是泛型中的通配符励稳,表示未知類型。通配符可以用作參數(shù)囱井,字段或局部變量的類型驹尼,有時還可以用作返回類型。在調(diào)用通用方法或?qū)嵗ㄓ妙悤r庞呕,不能使用通配符新翎。在以下各節(jié)中,我們將學習上界通配符住练,下界通配符和通配符捕獲地啰。

9.1)Java泛型上界通配符

上限通配符用于在方法中放寬對變量類型的限制。假設我們要編寫一個將返回列表中數(shù)字總和的方法讲逛,那么我們的實現(xiàn)將是這樣的亏吝。

現(xiàn)在,上述實現(xiàn)的問題在于它不適用于Integers或Doubles盏混,因為我們知道List 和List 不相關(guān)蔚鸥,這在使用高層通配符時很有用惜论。我們將通用通配符與extends關(guān)鍵字和上級類或接口一起使用,這將允許我們傳遞上級子類類型的參數(shù)止喷。

public static double sum(List<Number> list){
    double sum = 0;
    for(Number n : list){
        sum += n.doubleValue();
    }
    return sum;
}

可以像下面的程序一樣修改上面的實現(xiàn)馆类。

package com.journaldev.generics;
import java.util.ArrayList;
import java.util.List;
public class GenericsWildcards {
    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        ints.add(3); ints.add(5); ints.add(10);
        double sum = sum(ints);
        System.out.println("Sum of ints="+sum);
    }
    public static double sum(List<? extends Number> list){
        double sum = 0;
        for(Number n : list){
            sum += n.doubleValue();
        }
        return sum;
    }
}

就像按照接口編寫代碼一樣,在上述方法中弹谁,我們可以使用上限類號碼的所有方法蹦掐。請注意,對于上界列表僵闯,除空之外卧抗,我們不允許將任何對象添加到列表中。如果我們嘗試在sum方法內(nèi)將元素添加到列表中鳖粟,則該程序?qū)o法編譯社裆。

9.2)Java泛型無限制通配符

有時,我們希望通用方法適用于所有類型向图,在這種情況下泳秀,可以使用無界通配符。與使用<榄攀?extends Object>嗜傅。

public static void printData(List<?> list){
    for(Object obj : list){
        System.out.print(obj + "::");
    }
}

我們可以為PrintData方法提供List 或List 或任何其他類型的Object列表參數(shù)。與上限列表類似檩赢,我們可以在列表中添加任何內(nèi)容吕嘀。

9.3)Java泛型下界通配符

假設我們要在方法中將整體添加到整數(shù)列表中,我們可以將參數(shù)類型保持為List贞瞒,但可以與Integers捆綁在一起偶房,而List 和List 也可以容納整數(shù),因此我們可以使用下限通配符來實現(xiàn)军浆。我們使用超級關(guān)鍵字和下限類的泛型通配符(棕洋?)來實現(xiàn)此目的。

我們可以傳遞下界或下界的任何超類型作為參數(shù)乒融,在這種情況下掰盘,java編譯器允許將下界對象類型添加到列表中。

public static void addIntegers(List<? super Integer> list){
    list.add(new Intege(50));
}

10.使用泛型通配符進行子類型化

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a subtype of List<? extends Number>

11. Java泛型類型重構(gòu)

添加了Java泛型以在編譯時提供類型檢查赞季,并且在運行時沒有使用愧捕,因此Java編譯器使用類型更改功能刪除字節(jié)碼中的所有泛型類型檢查代碼,并在必要時插入類型轉(zhuǎn)換碟摆。類型定義可確保不會為參數(shù)化類型創(chuàng)建新的類晃财;因此,泛型不會產(chǎn)生運行時浪費典蜕。

例如断盛,如果我們有如下通用類;

public class Test<T extends Comparable<T>> {
    private T data;
    private Test<T> next;
    public Test(T d, Test<T> n) {
        this.data = d;
        this.next = n;
    }
    public T getData() { return this.data; }
}

Java編譯器用第一個綁定接口Comparable替換有界類型參數(shù)T愉舔,如下代碼:

public class Test {
    private Comparable data;
    private Test next;
    public Node(Comparable d, Test n) {
        this.data = d;
        this.next = n;
    }
    public Comparable getData() { return data; }
}

12.泛型常見問題解答

12.1)為什么我們在Java中使用泛型钢猛?

泛型提供了強大的編譯時類型檢查,并降低了ClassCastException和顯式對象轉(zhuǎn)換的風險轩缤。

12.2)泛型中的T是什么命迈?

我們使用創(chuàng)建通用類,接口和方法火的。我們在使用T時將其替換為實際類型壶愤。

12.3)泛型如何在Java中工作?

通用代碼可確保類型安全馏鹤。編譯器使用類型預先在編譯時刪除所有類型參數(shù)征椒,以減少運行時的重載。

13. Java泛型–進一步閱讀

  • 泛型不支持子類型湃累,因此List numbers = new ArrayList();將不進行編譯
  • 我們無法創(chuàng)建通用副本勃救,因此List[] array = new ArrayList[10]無法編譯

這是所有的Java泛型,Java泛型是非常龐大的治力,需要大量的時間來了解和有效地使用它蒙秒。本文提供了泛型的基本細節(jié),以及如何使用泛型來擴展程序的類型安全性宵统。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晕讲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子马澈,更是在濱河造成了極大的恐慌益兄,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箭券,死亡現(xiàn)場離奇詭異净捅,居然都是意外死亡,警方通過查閱死者的電腦和手機辩块,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門蛔六,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人废亭,你說我怎么就攤上這事国章。” “怎么了豆村?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵液兽,是天一觀的道長。 經(jīng)常有香客問我,道長四啰,這世上最難降的妖魔是什么宁玫? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮柑晒,結(jié)果婚禮上欧瘪,老公的妹妹穿的比我還像新娘。我一直安慰自己匙赞,他們只是感情好佛掖,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涌庭,像睡著了一般芥被。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坐榆,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天拴魄,我揣著相機與錄音,去河邊找鬼猛拴。 笑死羹铅,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的愉昆。 我是一名探鬼主播职员,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼跛溉!你這毒婦竟也來了焊切?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤芳室,失蹤者是張志新(化名)和其女友劉穎专肪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堪侯,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嚎尤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了伍宦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芽死。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖次洼,靈堂內(nèi)的尸體忽然破棺而出关贵,到底是詐尸還是另有隱情,我是刑警寧澤卖毁,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布揖曾,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏炭剪。R本人自食惡果不足惜练链,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望念祭。 院中可真熱鬧兑宇,春花似錦碍侦、人聲如沸粱坤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽站玄。三九已至,卻和暖如春濒旦,著一層夾襖步出監(jiān)牢的瞬間株旷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工尔邓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晾剖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓梯嗽,卻偏偏與公主長得像齿尽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子灯节,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內(nèi)容