從0開(kāi)始復(fù)習(xí)java(6)--GenericType

Java5增加泛型支持在很大程度上都是為了讓集合能記住其元素的數(shù)據(jù)類型。在沒(méi)有泛型之前明未,一旦把一個(gè)對(duì)象放進(jìn)集合中,集合就會(huì)忘記對(duì)象的類型,把所有的對(duì)象當(dāng)成Object類型處理萍鲸。當(dāng)程序從集合中取出對(duì)象后,就需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換擦俐。而泛型可以讓集合記住其元素的數(shù)據(jù)類型脊阴。

一、認(rèn)識(shí)泛型

java7之前的語(yǔ)法:

List<String> strList = new ArrayList<String>();
Map<String,String> scores = new HashMap<String,String>();

從Java7開(kāi)始使用菱形語(yǔ)法:

List<String> strList = new ArrayList<>()

二蚯瞧、深入泛型

泛型:允許在定義類嘿期、接口、方法時(shí)使用類型形參埋合,這個(gè)類型形參將在聲明變量备徐、創(chuàng)建對(duì)象、調(diào)用方法時(shí)動(dòng)態(tài)地指定甚颂。Java5為集合框架中的全部接口和類增加了泛型支持蜜猾。

定義泛型接口、類

//定義接口時(shí)指定類型形參
public interface List<E>{
    void add(E x);
    Iterator<E> iterator();
}
public interface Iterator<E>{
    E next();
    boolean hasNext();
}
public interface Map<K,V>{
    Set<K> keySet();
    V put(K key, V value){
        
    }
}

Iterator<E>振诬、Set<K>表面他們是一種特殊的數(shù)據(jù)類型蹭睡,是一種和IteratorSet不同的數(shù)據(jù)類型赶么,可以認(rèn)為是他們的子類肩豁。

包含泛型聲明的類型可以在定義變量、創(chuàng)建對(duì)象時(shí)傳入一個(gè)類型參數(shù)辫呻,從而可以動(dòng)態(tài)地生成無(wú)數(shù)多個(gè)邏輯上的子類清钥,但這種子類在物理上并不存在。

創(chuàng)建帶泛型聲明的自定義類放闺,為該類定義構(gòu)造器時(shí)循捺,構(gòu)造器名還是原來(lái)的類名,不要增加泛型聲明雄人。

從泛型類派生子類

//錯(cuò)誤的
public class A extends Apple<T>{}
//正確的
public class A extends Apple<String>{}
//會(huì)出現(xiàn)警告
public class A extends Apple{}

并不存在泛型類

//l1.getClass()==l2.getClass()
List<String> l1 = new ArrayList<>();
List<String> l1 = new ArrayList<>();

不管為泛型形參傳入哪一種類型的實(shí)參从橘,他們依然被當(dāng)作同一個(gè)類處理,在內(nèi)存中也只占用一塊內(nèi)存空間础钠,因此在靜態(tài)方法恰力、靜態(tài)初始化塊或者靜態(tài)變量的聲明和初始化中不允許使用類型形參。

public class R<T>{
    //錯(cuò)誤旗吁,不能在靜態(tài)變量聲明中使用類型形參
    static T info;
    T age;
    public void foo(T msg){}
    //錯(cuò)誤踩萎,不能在靜態(tài)方法聲明中使用類型形參
    public static void var(T msg){}
}
java.util.Collection<String> cs = new java.util.ArrayList<>();
//錯(cuò)誤,并不會(huì)生成泛型類
if (cs instanceof java.util.ArrayList<String>){}

三很钓、類型通配符

//會(huì)有泛型警告
public void test(List c){
    for(int i=0; i<c.size(); i++){
        System.out.println(c.get(i));
    }
}

改成

//會(huì)有泛型警告
public void test(List<Object> c){
    for(int i=0; i<c.size(); i++){
        System.out.println(c.get(i));
    }
}
List<String> str = new ArrayList<>();
test(str);

上面代碼產(chǎn)生異常香府。

無(wú)法將Test中的test(java.util.List<java.lang.Object>)應(yīng)用于(java.util.List<java.lang.String>)

如果FooBar的子類型(子類或者子接口)董栽,而G是一個(gè)具有泛型聲明的類或接口,G<Foo>并不是G<Bar>的子類型企孩。

數(shù)組與泛型不同锭碳,Foo[]bar[]的子類型。

使用類型通配符

//其類型為Object
public void test(List<?> c){
    for(int i=0; i<c.size(); i++){
        System.out.println(c.get(i));
    }
}

c中包含的是Object勿璃。

這種帶通配符的List僅表明它是各種泛型List的父類擒抛,并不能把元素加入進(jìn)去。

List<?> c = new ArrayList<String>();
//編譯錯(cuò)誤
c.add(new Object());

c中放的類型是Object补疑,而add的參數(shù)是E類的對(duì)象或者其子類的對(duì)象歧沪。該例中不知道E是什么類型,產(chǎn)生編譯錯(cuò)誤莲组。null是所有引用類型的實(shí)例诊胞,它可以添加進(jìn)去。

get返回值是一個(gè)未知類型锹杈,可以賦值給Object類型的變量厢钧。

設(shè)定類型通配符的上限

public abstract class Shape{
    public abstract void draw();
}
public class Circle extends Shape{
    public void draw(Canvas c){
        System.out.println(c+"圓");
    }
}
public class Rectangle extends Shape{
    public void draw(Canvas c){
        System.out.println(c+"長(zhǎng)方形");
    }
}
public class Canvas{
    public void drawAll(List<? extends Shape> shapes){
        for (Shape shape:shapes){
            shape.draw(this);
        }
    }
}
List<Circle> c = new ArrayList<>();
Canvas cv = new Canvas();
c.drawAll(c);

以上程序會(huì)將List<Circle>對(duì)象當(dāng)成List<? extends Shape>使用。List<? extends Shape>可以表示他們的父類--只要List尖括號(hào)里的類型是Shape的子類型即可嬉橙。

當(dāng)前程序無(wú)法確定這個(gè)受限制的通配符的具體類型早直。所以不能把Shape對(duì)象或者其子類型的對(duì)象加入這個(gè)泛型集合中。

設(shè)定類型形參的上限

public class Apple<T extends Number>{
    T col;
    public static void main(String[] args){
        Apple<Integer> a = new Apple<Integer>();
        //報(bào)錯(cuò)
        Apple<String> b = new Apple<String>();
    }
}

程序可以為類型形參設(shè)置多個(gè)上限(至多一個(gè)父類上限市框,可以有多個(gè)接口上限)霞扬。

public class Apple<T extends Number&java.io.Serializable>{

}

和類同時(shí)繼承父類和實(shí)現(xiàn)接口相似,所有的接口上限必須位于類上限之后枫振。

四喻圃、泛型方法

泛型方法的格式如下:

修飾符 <T,S> 返回值類型 方法名(形參){
    //方法體
}
import java.util.ArrayList;
import java.util.Collection;

import com.sun.org.apache.xpath.internal.operations.Number;

public class GenericMethodTest{
    static <T> void fromArrayToCollection(T[] t, Collection<T> c){
        for(T o: t){
            c.add(o);
        }
    }
    public static void main(String[] args) {
        Object[] oa = new Object[100];
        Collection<Object> co = new ArrayList<>();

        String[] sa = new String[100];
        Collection<String> cs = new ArrayList<>();

        //T為Object類型
        fromArrayToCollection(sa, co);

        Integer[] ia = new Integer();
        Collection<Number> cn = new ArrayList<>();

        //T為Number類型
        fromArrayToCollection(ia, cn);

    }
}

泛型方法和類型通配符的區(qū)別

大多數(shù)時(shí)候可以使用泛型方法代替類型通配符。

public interface Collection<E>{
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);

    ...
}

采用泛型方法實(shí)現(xiàn):

public interface Collection<E>{
    <T> boolean containsAll(Collection<T> c);
    <T extends E> boolean addAll(Collection<T> c);

    ...
}

通配符被設(shè)計(jì)用來(lái)支持靈活的子類化的粪滤。

泛型方法允許類型形參被用來(lái)表示方法的一個(gè)或多個(gè)參數(shù)之間的類型依賴關(guān)系斧拍,或者方法返回值與參數(shù)之間的類型依賴關(guān)系。如果沒(méi)有這樣的類型依賴關(guān)系杖小,就不應(yīng)該使用泛型方法肆汹。

如果又需要,也可以同時(shí)使用泛型方法和通配符:

public class Collections{
    public static <T> void copy(List<T> dest, List<? extends T> src){}
}

可以使用下面的泛型方法替換:

public static <T, S extends T> void copy(List<T> dest, List<S> src)

S僅使用了一次予权,其他參數(shù)的類型和方法返回值都不依賴于它昂勉,沒(méi)有存在的必要,可以用通配符替換扫腺。

顯著區(qū)別:
類型通配符既可以在方法簽名中定義形參的類型岗照,也可以用于定義變量的類型;但泛型方法中的類型形參必須在對(duì)應(yīng)方法中顯示聲明。

菱形語(yǔ)法與泛型構(gòu)造器

java允許在構(gòu)造器簽名中聲明類型形參攒至。

class Foo{
    public <T> Foo(T t){
        ...
    }
}
class Foo <E>{
    public <T> Foo(T t){

    }
}
public class GenericTest{
    public static void main(String[] args){
        Foo<String> s1 = new Foo<>(5);
        Foo<String> s2 = new <Integer> Foo<String>(5);
        //下面代碼出錯(cuò)
        Foo<String> s3 = new <Integer> Foo<>();
    }
}

設(shè)定通配符下限

 public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    int srcSize = src.size();
    if (srcSize > dest.size())
        throw new IndexOutOfBoundsException("Source does not fit in dest");

    if (srcSize < COPY_THRESHOLD ||
        (src instanceof RandomAccess && dest instanceof RandomAccess)) {
        for (int i=0; i<srcSize; i++)
            dest.set(i, src.get(i));
    } else {
        ListIterator<? super T> di=dest.listIterator();
        ListIterator<? extends T> si=src.listIterator();
        for (int i=0; i<srcSize; i++) {
            di.next();
            di.set(si.next());
        }
    }
}

java8改進(jìn)的類型推斷

  • 可通過(guò)調(diào)用方法的上下文來(lái)推斷類型參數(shù)的目標(biāo)類型
  • 可在方法調(diào)用鏈中厚者,將推斷得到的類型參數(shù)傳遞到最后一個(gè)方法。

五迫吐、擦除和轉(zhuǎn)換

如果沒(méi)有為一個(gè)泛型類制定實(shí)際的類型參數(shù)库菲,則該類型參數(shù)被成為raw type,默認(rèn)是聲明該類型參數(shù)時(shí)制定的第一個(gè)上限類型渠抹。

public class Test{
    public static void main(String[] args){
        List<Integer> li = new ArrayList<>();
        List list=li;
        //擦除,提示"未經(jīng)檢查的轉(zhuǎn)換"
        List<String> li = list;
        //運(yùn)行時(shí)異常
        System.out.println(li.get(0));
    }
}

六闪萄、泛型與數(shù)組

數(shù)組的類型不可以是類型變量梧却,除非是采用通配符的方式。

數(shù)組元素類型不能包含泛型變量或者泛型形參败去,除非是無(wú)上限的泛型通配符放航,但可以聲明元素類型包含泛型變量或泛型形參的數(shù)組。

//不允許
List<String>[] lsa = new List<String>[10];
//允許
List<String>[] lsa = new ArrayList[10];
Object[] oa = (Object[])lsa;
List<integer> li = new ArrayList<>();
li.add(3);
oa[1] = li;
//下面代碼將不會(huì)有警告圆裕,但引發(fā)ClassCastException
String s = lsa[1].get(0);

Object target = lsa[1].get(0);
if(target instanceof String){
    String s =(String)target;
}

創(chuàng)建元素類型是泛型變量的數(shù)組對(duì)象也會(huì)導(dǎo)致編譯錯(cuò)誤:

<T> T[] makeArray(Collection<T> coll){
    //下面語(yǔ)句出錯(cuò)
    return new T[coll.size()];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末广鳍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吓妆,更是在濱河造成了極大的恐慌赊时,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件行拢,死亡現(xiàn)場(chǎng)離奇詭異祖秒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)舟奠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門竭缝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)痘番,“玉大人熏纯,你說(shuō)我怎么就攤上這事∠蚁ぃ” “怎么了耿戚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵湿故,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我膜蛔,道長(zhǎng)晓锻,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任飞几,我火速辦了婚禮砚哆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己躁锁,他們只是感情好纷铣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著战转,像睡著了一般搜立。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上槐秧,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天啄踊,我揣著相機(jī)與錄音,去河邊找鬼刁标。 笑死颠通,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的膀懈。 我是一名探鬼主播顿锰,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼启搂!你這毒婦竟也來(lái)了硼控?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胳赌,失蹤者是張志新(化名)和其女友劉穎牢撼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體疑苫,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浪默,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缀匕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纳决。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖乡小,靈堂內(nèi)的尸體忽然破棺而出阔加,到底是詐尸還是另有隱情,我是刑警寧澤满钟,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布胜榔,位于F島的核電站,受9級(jí)特大地震影響湃番,放射性物質(zhì)發(fā)生泄漏夭织。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一吠撮、第九天 我趴在偏房一處隱蔽的房頂上張望尊惰。 院中可真熱鬧,春花似錦、人聲如沸弄屡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膀捷。三九已至迈嘹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間全庸,已是汗流浹背秀仲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壶笼,地道東北人神僵。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拌消,于是被迫代替她去往敵國(guó)和親挑豌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子安券,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • 開(kāi)發(fā)人員在使用泛型的時(shí)候墩崩,很容易根據(jù)自己的直覺(jué)而犯一些錯(cuò)誤。比如一個(gè)方法如果接收List作為形式參數(shù)侯勉,那么如果嘗試...
    時(shí)待吾閱讀 1,045評(píng)論 0 3
  • 一鹦筹、泛型簡(jiǎn)介1.引入泛型的目的 了解引入泛型的動(dòng)機(jī),就先從語(yǔ)法糖開(kāi)始了解址貌。 語(yǔ)法糖 語(yǔ)法糖(Syntactic S...
    Android進(jìn)階與總結(jié)閱讀 1,026評(píng)論 0 9
  • 一铐拐、泛型簡(jiǎn)介 1.引入泛型的目的 了解引入泛型的動(dòng)機(jī),就先從語(yǔ)法糖開(kāi)始了解练对。 語(yǔ)法糖 語(yǔ)法糖(Syntactic ...
    Ruheng閱讀 4,453評(píng)論 2 50
  • 一遍蟋、泛型的概念 泛型就是:類型參數(shù)化,處理的數(shù)據(jù)類型不是固定的螟凭,而是可以作為參數(shù)傳入虚青;瘋狂講義定義:泛型就是允許在...
    Serenity那年閱讀 699評(píng)論 2 0
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 10,934評(píng)論 6 13