泛型就這么簡單

前言

從今天開始進入Java基礎(chǔ)的復(fù)習,可能一個星期會有一篇的<十道簡單算法>励幼,我寫博文的未必都是正確的~如果有寫錯的地方請大家多多包涵并指正~

今天要復(fù)習的是泛型苹粟,泛型在Java中也是個很重要的知識點,本文主要講解基礎(chǔ)的概念苛秕,并不是高深的知識,如果基礎(chǔ)好的同學可以當復(fù)習看看~

一店煞、什么是泛型顷蟀?

Java泛型設(shè)計原則:只要在編譯時期沒有出現(xiàn)警告,那么運行時期就不會出現(xiàn)ClassCastException異常.

泛型:把類型明確的工作推遲到創(chuàng)建對象或調(diào)用方法的時候才去明確的特殊的類型

參數(shù)化類型:

  • 把類型當作是參數(shù)一樣傳遞
  • <數(shù)據(jù)類型> 只能是引用類型

相關(guān)術(shù)語:

  • ArrayList<E>中的E稱為類型參數(shù)變量
  • ArrayList<Integer>中的Integer稱為實際類型參數(shù)
  • 整個稱為ArrayList<E>泛型類型
  • 整個ArrayList<Integer>稱為參數(shù)化的類型ParameterizedType

二苟弛、為什么需要泛型

早期Java是使用Object來代表任意類型的,但是向下轉(zhuǎn)型有強轉(zhuǎn)的問題缤削,這樣程序就不太安全

首先亭敢,我們來試想一下:沒有泛型,集合會怎么樣

  • Collection扣溺、Map集合對元素的類型是沒有任何限制的。本來我的Collection集合裝載的是全部的Dog對象,但是外邊把Cat對象存儲到集合中,是沒有任何語法錯誤的荠医。
  • 把對象扔進集合中,集合是不知道元素的類型是什么的,僅僅知道是Object。因此在get()的時候禁谦,返回的是Object州泊。外邊獲取該對象,還需要強制轉(zhuǎn)換

有了泛型以后:

  • 代碼更加簡潔【不用強制轉(zhuǎn)換】
  • 程序更加健壯【只要編譯時期沒有警告弟孟,那么運行時期就不會出現(xiàn)ClassCastException異常】
  • 可讀性和穩(wěn)定性【在編寫集合的時候,就限定了類型】

2.1有了泛型后使用增強for遍歷集合

在創(chuàng)建集合的時候,我們明確了集合的類型了嘁信,所以我們可以使用增強for來遍歷集合卦溢!

        //創(chuàng)建集合對象
        ArrayList<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        //遍歷,由于明確了類型.我們可以增強for
        for (String s : list) {
            System.out.println(s);
        }

三、泛型基礎(chǔ)

3.1泛型類

泛型類就是把泛型定義在類上昏苏,用戶使用該類的時候洼专,才把類型明確下來....這樣的話棒掠,用戶明確了什么類型,該類就代表著什么類型...用戶在使用的時候就不用擔心強轉(zhuǎn)的問題屁商,運行時轉(zhuǎn)換異常的問題了句柠。

  • 在類上定義的泛型,在類的方法中也可以使用棒假!

/*
    1:把泛型定義在類上
    2:類型變量定義在類上,方法中也可以使用
 */
public class ObjectTool<T> {
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}
  • 測試代碼:

用戶想要使用哪種類型溯职,就在創(chuàng)建的時候指定類型。使用的時候帽哑,該類就會自動轉(zhuǎn)換成用戶想要使用的類型了谜酒。


    public static void main(String[] args) {
        //創(chuàng)建對象并指定元素類型
        ObjectTool<String> tool = new ObjectTool<>();

        tool.setObj(new String("鐘福成"));
        String s = tool.getObj();
        System.out.println(s);

        //創(chuàng)建對象并指定元素類型
        ObjectTool<Integer> objectTool = new ObjectTool<>();
        /**
         * 如果我在這個對象里傳入的是String類型的,它在編譯時期就通過不了了.
         */
        objectTool.setObj(10);
        int i = objectTool.getObj();
        System.out.println(i);
    }

3.2泛型方法

前面已經(jīng)介紹了泛型類了,在類上定義的泛型妻枕,在方法中也可以使用.....

現(xiàn)在呢僻族,我們可能就僅僅在某一個方法上需要使用泛型....外界僅僅是關(guān)心該方法,不關(guān)心類其他的屬性...這樣的話屡谐,我們在整個類上定義泛型述么,未免就有些大題小作了。

  • 定義泛型方法....泛型是先定義后使用的
    //定義泛型方法..
    public <T> void show(T t) {
        System.out.println(t);

    }
  • 測試代碼:

用戶傳遞進來的是什么類型愕掏,返回值就是什么類型了

    public static void main(String[] args) {
        //創(chuàng)建對象
        ObjectTool tool = new ObjectTool();

        //調(diào)用方法,傳入的參數(shù)是什么類型,返回值就是什么類型
        tool.show("hello");
        tool.show(12);
        tool.show(12.5);

    }

3.3泛型類派生出的子類

前面我們已經(jīng)定義了泛型類度秘,泛型類是擁有泛型這個特性的類,它本質(zhì)上還是一個Java類饵撑,那么它就可以被繼承

那它是怎么被繼承的呢剑梳??這里分兩種情況

  1. 子類明確泛型類的類型參數(shù)變量
  2. 子類不明確泛型類的類型參數(shù)變量

3.3.1子類明確泛型類的類型參數(shù)變量

  • 泛型接口

/*
    把泛型定義在接口上
 */
public interface Inter<T> {
    public abstract void show(T t);

}
  • 實現(xiàn)泛型接口的類.....

/**
 * 子類明確泛型類的類型參數(shù)變量:
 */

public class InterImpl implements Inter<String> {
    @Override
    public void show(String s) {
        System.out.println(s);

    }
}

3.3.2子類不明確泛型類的類型參數(shù)變量

  • 當子類不明確泛型類的類型參數(shù)變量時滑潘,外界使用子類的時候垢乙,也需要傳遞類型參數(shù)變量進來,在實現(xiàn)類上需要定義出類型參數(shù)變量

/**
 * 子類不明確泛型類的類型參數(shù)變量:
 *      實現(xiàn)類也要定義出<T>類型的
 *
 */
public class InterImpl<T> implements Inter<T> {

    @Override
    public void show(T t) {
        System.out.println(t);

    }
}

測試代碼:

    public static void main(String[] args) {
        //測試第一種情況
        //Inter<String> i = new InterImpl();
        //i.show("hello");

        //第二種情況測試
        Inter<String> ii = new InterImpl<>();
        ii.show("100");

    }

值得注意的是:

  • 實現(xiàn)類的要是重寫父類的方法语卤,返回值的類型是要和父類一樣的追逮!
  • 類上聲明的泛形只對非靜態(tài)成員有效

3.4類型通配符

為什么需要類型通配符?粹舵?钮孵??我們來看一個需求.......

現(xiàn)在有個需求:方法接收一個集合參數(shù)齐婴,遍歷集合并把集合元素打印出來油猫,怎么辦稠茂?

  • 按照我們沒有學習泛型之前柠偶,我們可能會這樣做:

public void test(List list){

    for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

上面的代碼是正確的情妖,只不過在編譯的時候會出現(xiàn)警告,說沒有確定集合元素的類型....這樣是不優(yōu)雅的...

  • 那我們學習了泛型了诱担,現(xiàn)在要怎么做呢毡证??有的人可能會這樣做:

public void test(List<Object> list){

    for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

這樣做語法是沒毛病的蔫仙,但是這里十分值得注意的是:該test()方法只能遍歷裝載著Object的集合A暇Α!摇邦!

強調(diào):泛型中的<Object>并不是像以前那樣有繼承關(guān)系的恤煞,也就是說List<Object>List<String>是毫無關(guān)系的!J┘>影恰!

那現(xiàn)在咋辦丑慎?喜喂??我們是不清楚List集合裝載的元素是什么類型的竿裂,List<Objcet>這樣是行不通的........于是Java泛型提供了類型通配符 ?

所以代碼應(yīng)該改成這樣:


public void test(List<?> list){

    for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

?號通配符表示可以匹配任意類型玉吁,任意的Java類都可以匹配.....

現(xiàn)在非常值得注意的是,當我們使用?號通配符的時候:就只能調(diào)對象與類型無關(guān)的方法腻异,不能調(diào)用對象與類型有關(guān)的方法进副。

記住,只能調(diào)用與對象無關(guān)的方法悔常,不能調(diào)用對象與類型有關(guān)的方法敢会。因為直到外界使用才知道具體的類型是什么。也就是說这嚣,在上面的List集合鸥昏,我是不能使用add()方法的。因為add()方法是把對象丟進集合中姐帚,而現(xiàn)在我是不知道對象的類型是什么吏垮。


3.4.1設(shè)定通配符上限

首先,我們來看一下設(shè)定通配符上限用在哪里....

現(xiàn)在罐旗,我想接收一個List集合膳汪,它只能操作數(shù)字類型的元素【Float、Integer九秀、Double遗嗽、Byte等數(shù)字類型都行】,怎么做鼓蜒?痹换?征字?

我們學習了通配符,但是如果直接使用通配符的話娇豫,該集合就不是只能操作數(shù)字了匙姜。因此我們需要用到設(shè)定通配符上限

    List<? extends Number>

上面的代碼表示的是:List集合裝載的元素只能是Number的子類或自身


    public static void main(String[] args) {

        //List集合裝載的是Integer,可以調(diào)用該方法
        List<Integer> integer = new ArrayList<>();
        test(integer);

        //List集合裝載的是String冯痢,在編譯時期就報錯了
        List<String> strings = new ArrayList<>();
        test(strings);

    }

    public static void test(List<? extends Number> list) {

    }

3.4.2設(shè)定通配符下限

既然上面我們已經(jīng)說了如何設(shè)定通配符的上限氮昧,那么設(shè)定通配符的下限也不是陌生的事了。直接來看語法吧

    //傳遞進來的只能是Type或Type的父類
    <? super Type>

設(shè)定通配符的下限這并不少見浦楣,在TreeSet集合中就有....我們來看一下

    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

那它有什么用呢袖肥??我們來想一下振劳,當我們想要創(chuàng)建一個TreeSet<String>類型的變量的時候昭伸,并傳入一個可以比較String大小的Comparator。

那么這個Comparator的選擇就有很多了澎迎,它可以是Comparator<String>庐杨,還可以是類型參數(shù)是String的父類,比如說Comparator<Objcet>....

這樣做夹供,就非常靈活了灵份。也就是說,只要它能夠比較字符串大小哮洽,就行了

值得注意的是:無論是設(shè)定通配符上限還是下限填渠,都是不能操作與對象有關(guān)的方法,只要涉及到了通配符鸟辅,它的類型都是不確定的氛什!

3.5通配符和泛型方法

大多時候,我們都可以使用泛型方法來代替通配符的.....


    //使用通配符
    public static void test(List<?> list) {

    }

    //使用泛型方法
    public <T> void  test2(List<T> t) {

    }

上面這兩個方法都是可以的.....那么現(xiàn)在問題來了匪凉,我們使用通配符還是使用泛型方法呢枪眉??

原則:

  • 如果參數(shù)之間的類型有依賴關(guān)系再层,或者返回值是與參數(shù)之間有依賴關(guān)系的贸铜。那么就使用泛型方法
  • 如果沒有依賴關(guān)系的,就使用通配符聂受,通配符會靈活一些.

3.6泛型擦除

泛型是提供給javac編譯器使用的蒿秦,它用于限定集合的輸入類型,讓編譯器在源代碼級別上蛋济,即擋住向集合中插入非法數(shù)據(jù)棍鳖。但編譯器編譯完帶有泛形的java程序后,生成的class文件中將不再帶有泛形信息碗旅,以此使程序運行效率不受到影響渡处,這個過程稱之為“擦除”镜悉。

3.6.1兼容性

JDK5提出了泛型這個概念,但是JDK5以前是沒有泛型的骂蓖。也就是泛型是需要兼容JDK5以下的集合的。

當把帶有泛型特性的集合賦值給老版本的集合時候川尖,會把泛型給擦除了登下。

值得注意的是:它保留的就類型參數(shù)的上限。


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

        //類型被擦除了叮喳,保留的是類型的上限被芳,String的上限就是Object
        List list1 = list;

如果我把沒有類型參數(shù)的集合賦值給帶有類型參數(shù)的集合賦值,這又會怎么樣馍悟?畔濒?


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

它也不會報錯,僅僅是提示“未經(jīng)檢查的轉(zhuǎn)換”


四锣咒、泛型的應(yīng)用

當我們寫網(wǎng)頁的時候侵状,常常會有多個DAO,我們要寫每次都要寫好幾個DAO毅整,這樣會有點麻煩趣兄。

這里寫圖片描述

那么我們想要的效果是什么呢?悼嫉?只寫一個抽象DAO艇潭,別的DAO只要繼承該抽象DAO,就有對應(yīng)的方法了戏蔑。

要實現(xiàn)這樣的效果蹋凝,肯定是要用到泛型的。因為在抽象DAO中总棵,是不可能知道哪一個DAO會繼承它自己鳍寂,所以是不知道其具體的類型的。而泛型就是在創(chuàng)建的時候才指定其具體的類型情龄。

  • 抽象DAO

public abstract class BaseDao<T> {

    //模擬hibernate....
    private Session session;
    private Class clazz;

    //哪個子類調(diào)的這個方法伐割,得到的class就是子類處理的類型(非常重要)
    public BaseDao(){
        Class clazz = this.getClass();  //拿到的是子類
        ParameterizedType  pt = (ParameterizedType) clazz.getGenericSuperclass();  //BaseDao<Category>
        clazz = (Class) pt.getActualTypeArguments()[0];
        System.out.println(clazz);

    }

    public void add(T t){
        session.save(t);
    }

    public T find(String id){
        return (T) session.get(clazz, id);
    }

    public void update(T t){
        session.update(t);
    }

    public void delete(String id){
        T t = (T) session.get(clazz, id);
        session.delete(t);
    }

}
  • 繼承抽象DAO,該實現(xiàn)類就有對應(yīng)的增刪改查的方法了刃唤。

CategoryDao


public class CategoryDao extends BaseDao<Category> {

}

BookDao


public class BookDao extends BaseDao<Book> {

}

五隔心、最后

泛型的基礎(chǔ)就介紹到這里了,如果以后有需要的話再進行深入研究吧~如果覺得該文章幫助到你尚胞,不妨點個贊硬霍,關(guān)注公眾號一波~

image

參考資料:

  • Core Java

如果文章有錯的地方歡迎指正,大家互相交流笼裳。習慣在微信看技術(shù)文章唯卖,想要獲取更多的Java資源的同學粱玲,可以關(guān)注微信公眾號:Java3y

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拜轨,隨后出現(xiàn)的幾起案子抽减,更是在濱河造成了極大的恐慌,老刑警劉巖橄碾,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卵沉,死亡現(xiàn)場離奇詭異,居然都是意外死亡法牲,警方通過查閱死者的電腦和手機史汗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拒垃,“玉大人停撞,你說我怎么就攤上這事〉课停” “怎么了戈毒?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長横堡。 經(jīng)常有香客問我副硅,道長,這世上最難降的妖魔是什么翅萤? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任恐疲,我火速辦了婚禮,結(jié)果婚禮上套么,老公的妹妹穿的比我還像新娘培己。我一直安慰自己,他們只是感情好胚泌,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布省咨。 她就那樣靜靜地躺著,像睡著了一般玷室。 火紅的嫁衣襯著肌膚如雪零蓉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天穷缤,我揣著相機與錄音敌蜂,去河邊找鬼。 笑死津肛,一個胖子當著我的面吹牛章喉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秸脱,長吁一口氣:“原來是場噩夢啊……” “哼落包!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摊唇,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤咐蝇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后巷查,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體有序,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年吮便,在試婚紗的時候發(fā)現(xiàn)自己被綠了笔呀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幢踏。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡髓需,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出房蝉,到底是詐尸還是另有隱情僚匆,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布搭幻,位于F島的核電站咧擂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏檀蹋。R本人自食惡果不足惜松申,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俯逾。 院中可真熱鬧贸桶,春花似錦、人聲如沸桌肴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坠七。三九已至水醋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彪置,已是汗流浹背拄踪。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拳魁,地道東北人宫蛆。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親耀盗。 傳聞我的和親對象是個殘疾皇子想虎,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353