2021-05-15

泛型的使用

集合沒有泛型的時候扫外,集合存放數(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-51749-1621087260272)]

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

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

格式

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

[圖片上傳失敗...(image-2bf134-1621087260272)]

可以看到凤瘦,在定義泛型后宿礁,添加 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)用。 這很不幸鬼贱, 因?yàn)檫@個方法所作的只是從這個 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);
}

可以看到,程序類型的轉(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閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件证薇,死亡現(xiàn)場離奇詭異,居然都是意外死亡匆篓,警方通過查閱死者的電腦和手機(jī)浑度,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸦概,“玉大人箩张,你說我怎么就攤上這事〈笆校” “怎么了先慷?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咨察。 經(jīng)常有香客問我论熙,道長,這世上最難降的妖魔是什么摄狱? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任脓诡,我火速辦了婚禮,結(jié)果婚禮上媒役,老公的妹妹穿的比我還像新娘祝谚。我一直安慰自己,他們只是感情好酣衷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布踊跟。 她就那樣靜靜地躺著,像睡著了一般鸥诽。 火紅的嫁衣襯著肌膚如雪商玫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天牡借,我揣著相機(jī)與錄音拳昌,去河邊找鬼。 笑死钠龙,一個胖子當(dāng)著我的面吹牛炬藤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碴里,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沈矿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咬腋?” 一聲冷哼從身側(cè)響起羹膳,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎根竿,沒想到半個月后陵像,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體就珠,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年醒颖,在試婚紗的時候發(fā)現(xiàn)自己被綠了妻怎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡泞歉,死狀恐怖逼侦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腰耙,我是刑警寧澤榛丢,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站沟优,受9級特大地震影響涕滋,放射性物質(zhì)發(fā)生泄漏睬辐。R本人自食惡果不足惜挠阁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溯饵。 院中可真熱鬧侵俗,春花似錦、人聲如沸丰刊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啄巧。三九已至寻歧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秩仆,已是汗流浹背码泛。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澄耍,地道東北人噪珊。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像齐莲,于是被迫代替她去往敵國和親痢站。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

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

  • 集合框架使用集合做什么集合的分類具體的實(shí)現(xiàn)類ListArrayListLinkedListSetTreeSetMa...
    han741閱讀 251評論 0 1
  • springMvc/springBoot/springCloud接受json對象作為傳入?yún)?shù)注意點(diǎn) 首先先看一下p...
    stayFAndH閱讀 364評論 0 0
  • Ajax介紹 Ajax是什么? 1选酗、Ajax :Asynchronous JavaScript and XML (...
    拾壹_pro閱讀 101評論 0 0
  • bug修復(fù)后可正常運(yùn)行 include<stdio.h> include<graphics.h> include<...
    不努力的小企鵝閱讀 183評論 0 1
  • 俄羅斯方塊(bug未修復(fù)前) include<stdio.h> include<graphics.h> inclu...
    不努力的小企鵝閱讀 167評論 0 1