泛型的使用

[TOC]

泛型的使用

集合沒有泛型的時候蜘渣,集合存放數(shù)據(jù)時都會丟失原來的類型,全部改為Object杰刽。這樣可以獲得良好的通用性菠发。但是取出的時候,就需要做類型轉(zhuǎn)換贺嫂,如果類型寫錯了滓鸠,轉(zhuǎn)換就會出現(xiàn)異常。為了有更好的安全性和可讀性第喳,Java在JDK1.5的時候加入了泛型糜俗。

泛型的應(yīng)用非常重要,在教學(xué)中曲饱,務(wù)必讓學(xué)生學(xué)會基本的使用:

  1. 在集合(List悠抹、Set、Map)上使用泛型
  2. 在通用類或者接口上使用泛型
  3. 在方法上使用泛型
  4. 明白什么是泛型擦除

泛型的作用

使用泛型機(jī)制編寫的程序代碼要比那些雜亂地使用Object 變量 扩淀,然后再進(jìn)行強(qiáng)制類型轉(zhuǎn)換的代碼具有更好的安全性和可讀性 楔敌。泛型對于集合類尤其有用 ,例如 驻谆,ArrayList就是一個無處不在的集合類

沒有泛型的代碼:

List list = new ArrayList();

list.add(123);
list.add("abc");
list.add(1>2);
list.add('E');
list.add(890.12);

for (int i = 0; i < list.size(); i++) {
    System.out.println( list.get(i) );
}

輸入什么類型卵凑,就輸出什么類型。但是我希望list里面存放的數(shù)據(jù)類型只有字符串怎么辦 柒竞?沒有代碼的情況下代碼是這樣的:

List list = new ArrayList();

list.add("123.A");
list.add("abc.def");

for (int i = 0; i < list.size(); i++) {
    String str = (String) list.get(i);
    System.out.println( str.split("\\.")[0] ) ;
}

但是list中如果有其他類型呢竭沫?

List list = new ArrayList();
        
list.add("123.A");
list.add(123.123);  // 這行數(shù)據(jù)就是一個浮點(diǎn)型
list.add("abc.def");

for (int i = 0; i < list.size(); i++) {
    String str = (String) list.get(i);
    System.out.println( str.split("\\.")[0] ) ;
}

[圖片上傳失敗...(image-881058-1621555283938)]

發(fā)生了類型轉(zhuǎn)換錯誤~~

為了解決這類問題往衷,使用泛型是不二之選。通過泛型可以限制集合中數(shù)據(jù)的類型黑忱,只有符合的類型才能放到集合中

格式

在聲明的集合類型后面跟上一對尖括號,實(shí)現(xiàn)的類型構(gòu)造器的小括號前面跟上一對尖括號织盼。里面寫上需要存放的數(shù)據(jù)類型

[圖片上傳失敗...(image-e90249-1621555283938)]

可以看到杨何,在定義泛型后,添加 123.123 浮點(diǎn)數(shù)時編譯器就開始報錯了沥邻。在使用泛型后危虱,代碼也不需要做類型強(qiáng)轉(zhuǎn)了。

List<String> list = new ArrayList<String>();

list.add("123.A");
list.add("abc.def");

for (int i = 0; i < list.size(); i++) {
    String str = list.get(i);
    System.out.println( str.split("\\.")[0] ) ;
}

Map的演示

Map<String, String> data = new HashMap<>();

data.put("name", "張三");
data.put("age", "11歲");
data.put("sex", "男");

for (Map.Entry<String, String> entry: data.entrySet()) {
    System.out.println( entry.getKey() +":"+ entry.getValue());
}

簡單的泛型類

在定義類 Pair 時唐全,在類名后跟上 <T>

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public  Pair(T first , T second){ 
        this.first = first; 
        this.second = second; 
    }
    
    public void setFirst(T newValue) {
        first = newValue;
    }

    public void setSecond(T newValue) {
        second = newValue;
    }

    public T getFirst() {
        return first;
    }

    public T getSecond() {
        return second;
    }
    
    @Override
    public String toString() {
        return "Pair [first=" + first + ", second=" + second + "]";
    }
}

使用

public static void main(String[] args) {

    System.out.println(new Pair<Integer>(1, 2));
    //輸出:   Pair [first=1, second=2]
    
    System.out.println(new Pair<String>("諸葛", "孔明"));
    //輸出:   Pair [first=諸葛, second=孔明]
}

使用泛型埃跷,就如同將原來類定義的 T 替換為了指定的類型版本一樣蕊玷,比如:

public class Pair {
    private Integer first;
    private Integer second;

    public Pair() {
        first = null;
        second = null;
    }

    public  Pair(Integer first , Integer second){ 
        this.first = first; 
        this.second = second; 
    }
    //....
}

類型參數(shù)就跟在方法或構(gòu)造函數(shù)中普通的參數(shù)一樣。就像一個方法有形式參數(shù)(formal value parameters)來描述它操作的參數(shù)的種類一樣弥雹,一個泛型聲明也有形式類型參數(shù)(formal type parameters)垃帅。當(dāng)一個方法被調(diào)用,實(shí)參(actual arguments)替換形參剪勿,方法體被執(zhí)行贸诚。當(dāng)一個泛型聲明被調(diào)用,實(shí)際類型參數(shù)(actual type arguments)取代形式類型參數(shù)厕吉。

泛型方法

在使用前酱固,我們先明確一下泛型的各種通配符:

  1. T:type 數(shù)據(jù)類型
  2. E:element 元素
  3. K:key 鍵
  4. V:value 值
  5. ?:未知類型

示例:

class ArrayAlg {

    public static <T> T getMiddle(T... a) {
        return a[a.length / 2];
    }
}

測試

同一個類的方法,使用不同類型的數(shù)組头朱,都可以正常得到數(shù)據(jù)

String[] array = {"123","345","456" , "567"};
String string = ArrayAlg.getMiddle(array);
System.out.println( string );

Integer[] array2 = {33,44,55,66,77,88};
System.out.println( ArrayAlg.getMiddle(array2) );

有限制的通配符

考慮一個簡單的畫圖程序运悲,它可以用來畫各種形狀,比如矩形和圓形项钮。 為了在程序中表示這些形狀班眯,你可以定義下面的類繼承結(jié)構(gòu):

// 抽象類
public abstract class Shape {
    public abstract void draw();
}

// 畫布
public class Canvas {
    public void draw(Shape s) {
        s.draw();
    }
}

/// ---- 抽象類的子類-----------------------

public class Circle extends Shape {
    
    private int x, y, radius;

    public void draw() { 
        // ...
    }
}

public class Rectangle extends Shape {
    private int x, y, width, height;

    public void draw() {
        // ... 
    }
}

所有的圖形通常都有很多個形狀。假定它們用一個 list 來表示烁巫,Canvas 里有一個方法來畫出所有的形狀會比較方便

import java.util.List;

public class Canvas {
    public void draw(Shape s) {
        s.draw();
    }

    public void drawAll(List<Shape> shapes) {
        for (Shape s : shapes) {
            s.draw();
        }
    }
}

現(xiàn)在署隘,類型規(guī)則導(dǎo)致 drawAll()只能使用 Shape的list 來調(diào)用。它不能程拭,比如說對 List<Circle>來調(diào)用定踱。 這很不幸, 因為這個方法所作的只是從這個 list 讀取 shape恃鞋,因此它應(yīng)該也能對 List<Circle>調(diào)用崖媚。我們真正要的是這個方法能夠接受一個任意種類的 shape

import java.util.List;

public class Canvas {
    public void draw(Shape s) {
        s.draw();
    }

    // 注意方法參數(shù)的變化
    public void drawAll(List<? extends Shape> shapes) { 
        for (Shape s : shapes) {
            s.draw();
        }
    }
}

我們把類型 List<Shape> 替換成了 List<? extends Shape>。現(xiàn)在drawAll()可以接受任何 Shape 的子類的 List恤浪,所以我們可以對 List<Circle>進(jìn)行調(diào)用

List<? extends Shape>是有限制通配符的一個例子畅哑。這里?代表一個未知的類型水由,就像我們前面看到的通配符一樣荠呐。但是,在這里砂客,我們知道這個未知的類型實(shí)際上是Shape 的一個子類(它可以是 Shape本身或者 Shape 的子類而不必是 extends 自 Shape)泥张。我們說 Shape是這個通配符的上限(upper bound)。
像平常一樣鞠值,要得到使用通配符的靈活性有些代價媚创。這個代價是,現(xiàn)在向 shapes 中寫入是非法的彤恶。比如下面的代碼是不允許的

public void addRectangle(List<? extends Shape> shapes) { 
   //   編譯時會報錯 
   shapes.add( new Rectangle()); 
}

shapes.add 的第二個參數(shù)類型是? extends Shape ——一個 Shape 未知的子類钞钙。因此我們不知道這個類型是什么鳄橘,我們不知道它是不是 Rectangle 的父類;它可能是也可能不是一個父類芒炼,所以這里傳遞一個 Rectangle 不安全

擦除和翻譯

先看下面的代碼

public static String loophole(Integer x) {
    List<String> ys = new LinkedList<String>();
    List xs = ys;
    xs.add(x); 
    return ys.iterator().next();
}

public static void main(String[] args) {
    loophole(123);
}

[圖片上傳失敗...(image-8e32db-1621555283938)]

可以看到瘫怜,程序類型的轉(zhuǎn)換異常,但是編譯器卻沒有報錯

這樣的原因是本刽,泛型是通過 java 編譯器的稱為擦除(erasure)的前端處理來實(shí)現(xiàn)的鲸湃。你可以(基本上就是)把它認(rèn)為是一個從源碼到源碼的轉(zhuǎn)換,它把泛型版本的 loophole()轉(zhuǎn)換成非泛型版本盅安。 結(jié)果是唤锉,java 虛擬機(jī)的類型安全和穩(wěn)定性決不能冒險,即使在又unchecked warning 的情況下别瞭。

擦除去掉了所有的泛型類型信息。所有在尖括號之間的類型信息都被扔掉了株憾,因此蝙寨,比如說一個 List<String>類型被轉(zhuǎn)換為 List。所有對類型變量的引用被替換成類型變量的上限(通常是 Object)

Java 的泛型支持僅在語法級別

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗤瞎,一起剝皮案震驚了整個濱河市墙歪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贝奇,老刑警劉巖虹菲,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異掉瞳,居然都是意外死亡毕源,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門陕习,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霎褐,“玉大人,你說我怎么就攤上這事该镣《沉В” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵损合,是天一觀的道長省艳。 經(jīng)常有香客問我,道長嫁审,這世上最難降的妖魔是什么跋炕? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮土居,結(jié)果婚禮上枣购,老公的妹妹穿的比我還像新娘嬉探。我一直安慰自己,他們只是感情好棉圈,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布涩堤。 她就那樣靜靜地躺著,像睡著了一般分瘾。 火紅的嫁衣襯著肌膚如雪胎围。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天德召,我揣著相機(jī)與錄音白魂,去河邊找鬼。 笑死上岗,一個胖子當(dāng)著我的面吹牛福荸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肴掷,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼敬锐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呆瞻?” 一聲冷哼從身側(cè)響起台夺,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痴脾,沒想到半個月后颤介,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赞赖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年滚朵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薯定。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡始绍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出话侄,到底是詐尸還是另有隱情亏推,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布年堆,位于F島的核電站吞杭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏变丧。R本人自食惡果不足惜芽狗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痒蓬。 院中可真熱鬧童擎,春花似錦滴劲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芯砸,卻和暖如春萧芙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背假丧。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工双揪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人包帚。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓渔期,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渴邦。 傳聞我的和親對象是個殘疾皇子擎场,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

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

  • 集合沒有泛型的時候,集合存放數(shù)據(jù)時都會丟失原來的類型几莽,全部改為Object。這樣可以獲得良好的通用性宅静。但是取出的時...
    秋北_55a3閱讀 501評論 0 1
  • 泛型是Java中一項十分重要的特性章蚣,在Java 5版本被引入,在日常的編程過程中姨夹,有很多依賴泛型的場景纤垂,尤其是在集...
    zhipingChen閱讀 3,007評論 1 6
  • 本部分主要介紹:Java 的泛型、泛型通配符使用的規(guī)則和注意事項 泛型總結(jié) 泛型是 JDK 1.5 出現(xiàn)的技術(shù)磷账,是...
    sshentree閱讀 802評論 0 2
  • Java泛型使用示例整理 目標(biāo) Java泛型編程是JDK1.5版本后引入的峭沦。泛型讓編程人員能夠使用類型抽象,通常用...
    信海龍閱讀 8,934評論 0 30
  • 泛型的概念 泛型逃糟,本質(zhì)是參數(shù)化類型吼鱼,這個概念在程序設(shè)計里面很經(jīng)常可以看到绰咽,泛型的使用很靈活而且高效菇肃,它允許程序員將...
    MarcoHorse閱讀 253評論 0 0