国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【從基礎(chǔ)學(xué) Java】泛型

huhud / 398人閱讀

摘要:泛型方法泛型類中可以定義靜態(tài)非靜態(tài)的泛型方法。上述泛型類會被替換成下面形式一般使用第一個(gè)限定類型替換變?yōu)樵碱愋停瑳]有限定類型,使用替換。

引言

在面向?qū)ο蟮氖澜缋铮覀內(nèi)绻枰粋€(gè)容器來盛裝對象。舉個(gè)例子:一個(gè)籃子。我們可以用這個(gè)籃子裝蘋果,也可以用這個(gè)籃子裝香蕉。基于 OOP 的思想,我們不希望為蘋果和香蕉分別創(chuàng)建不同的籃子;同時(shí),我們希望放進(jìn)籃子里的是蘋果,拿出來的還是蘋果。于是,Java 程序員提出了「泛型」的概念——一種類似于 C++ 模板的技術(shù)。

早期程序員使用如下代碼創(chuàng)建一個(gè)泛型集合:

public class ArrayList{
    private Object[] elementData;
    ...
    public Object get(int i);
    public void add(Object o);
}

我們可以看出,對與這個(gè)集合而言,取出 (get) 和放入時(shí)都沒有進(jìn)行類型檢查。因此,如果我們不記得放入的順序,把取出的對象進(jìn)項(xiàng)強(qiáng)制類型轉(zhuǎn)換,很可能出現(xiàn) ClassCastException。因此,真正的泛型是可以在編譯時(shí)期,對數(shù)據(jù)類型進(jìn)行檢查,保證安全。如下面代碼所示:

ArrayList list = new ArrayList<>();

P.S. <>里的String叫做類型參數(shù)。

使用泛型,為我們提供了如下優(yōu)點(diǎn):

更強(qiáng)大的編譯時(shí)期的類型檢查

避免不必要的類型轉(zhuǎn)換,如:

List list = new ArrayList<>(3);
String str = list.get(0);

讓程序能夠?qū)崿F(xiàn)通用的算法

泛型類

泛型中使用名為泛型參數(shù)表明可以傳入類或方法的對象類型,這種設(shè)計(jì)實(shí)現(xiàn)了類型參數(shù)化(可以把同一類型的類作為參數(shù)進(jìn)行傳遞),如下面的代碼所示:

泛型類示例

public class Pair{
    private T first;
    private T last;
    
    public Pair(){}
    public Pair(T first, T last){
        this.first = frist;
        this.last = last;
    }
    
    public T getFirst();
    public T getLast();
}

泛型方法示例

public class Util{
    // 簡單的泛型方法
    public static  T getMiddle(T...a){
        return a[a.length/2];
    }
    // 帶限定符的泛型方法,如果有多個(gè)限定符,使用 & 連接多個(gè)接口或超類
    public static  T min(T...a){
        // 具體實(shí)現(xiàn)
    }
}

注意,這里的泛型參數(shù)(type parameter)在上述示例中指的是用大寫字母 T 表示的值,而泛型實(shí)參(type argument)則是指 T a 中的 a。根據(jù)慣例,泛型參數(shù)通常如下命名:

E:表示一個(gè)元素,Java 集合框架中使用最多

K:鍵

N:數(shù)字

T:類型

V:值

S,U,V:其它類型

原始類型(raw type)

原始類型指的是,不包括泛型參數(shù)的類型,如上述泛型類中的 Pair。我們可以通過原生類型構(gòu)造對象:

Pair pair = new Pair();

同時(shí),可以通過泛型參數(shù)構(gòu)造對象:

Pair pair = new Pair<>();

但是,如果把一個(gè)通過原生類型獲取的對象指向一個(gè)通過泛型參數(shù)生成的參數(shù)會報(bào) unchecked warning,如下面的代碼:

Pair pair = new Pair();
Pair pair1 = pair;
繼承和子類型

在 Java 中,有繼承的概念,簡而言之,就是一個(gè)類型可以指向它的兼容類型,如:

Object object = new Object();
Integer integer = new Integer(20);
object = integer;

上述代碼表示:Integer IS-A Object。這種概念在泛型中也適用。如下定義:

public class Box{
    public void add(T t);
}

那么一個(gè) Box 的對象可以增加任意 Number 子類的值。但是 BoxBox 不是同一個(gè)類型。

泛型方法

泛型類中可以定義靜態(tài)、非靜態(tài)的泛型方法。泛型方法的語法為:<泛型參數(shù)類型列表> + 返回類型 + 泛型參數(shù)列表。

靜態(tài)方法

public static  void foo(T t){
}

非靜態(tài)方法

public void foo(T t){
}
類型限定

在某種情況下,我們希望方法只接受特定類型的參數(shù),可以使用如下語法實(shí)現(xiàn):

public  void inspect(U u){
    // 這里是邏輯處理
}

上述代碼中,該泛型方法只接受為 Number 類型的參數(shù)。同樣,也可以在泛型類上加以限制:

public class Utils{
    // 這里的 T 必須為 Number 類型
    private T t;
}

當(dāng)然,也可以使用多重限制,如下面代碼所示:

public class Utils{

}

P.S. 限制中的類必須放在接口的前面。

類型推斷

類型推斷是:編譯器去推斷調(diào)用方法的參數(shù)的類型的能力。
如,泛型方法中:

public  void addBox(Box box){
    // 這里是處理代碼
}

不必通過 obj.addBox(box) 調(diào)用, 可以省略。

構(gòu)造方法中:

// 類型推斷
Map> map = new HashMap<>();

其中,構(gòu)造方法中的泛型還可以這樣用:

// 定義泛型類
public class Box{
    public  Box(T t){
    
    }
}
// 實(shí)例化一個(gè)對象
public class Application{
    void method(){
        Box box = new Box<>(");
    }
}
通配符

通配符 ? 表示一個(gè)未知的類型,可用于參數(shù)的類型、字段以及局部變量中,但不可用于調(diào)用泛型方法里的類型參數(shù)、泛型對象實(shí)例化以及泛型超類里。

// 可以
public void foo(Pair pair){
    // 可以
    Pair foo;
}
// 可以
private Pair pair; 
上界通配符

上界通配符表明需要最高限定的類型,下面的代碼用來計(jì)算所有類型為數(shù)字的集合的總和:

public double sumList(List){
    // 這里做邏輯處理
}
無界限通配符

使用無界限通配符表示不確定的類型,以下兩種情況可以使用無界限通配符:

當(dāng)方法的參數(shù)可以用 Object 對象替換

方法的實(shí)現(xiàn)不依賴具體的類型

比如,有一個(gè)打印集合對象的方法:

// 定義一個(gè)打印集合對象列表的方法
public void printList(List list){
    for(Object obj: list){
        // 打印list
    }
}
// 調(diào)用方法
List integers = Arrays.asList(1,2,3);
List strings = Arrays.asList("A","B","C");
printList(integers);
printList(strings);

P.S. ListList 不同,List 只能插入 nullList 可以插入任何對象。

下界通配符

使用下界統(tǒng)配符,表明最低限度的類型,如:

public double sumList(List){
    // 這里做邏輯處理
}
通配符和子類型

在本文的繼承和子類里,提到過:Box 不是 Box 的子類。在 Java 泛型中,繼承關(guān)系可以通過如下圖表示:

可以看出,泛型中的 extends 的確限定了上界(父類);super 的確限定了下界(子類型);? 是所有泛型的超類(類似 Object)。

泛型的繼承關(guān)系(父子類型關(guān)系)可以通過下面的韋恩圖解釋:

我們不妨用某一泛型所占的面積表示其層次關(guān)系,面積大的在繼承關(guān)系上層次高。由上圖很容易看出: 的繼承層次比 的繼承層次高;相應(yīng)地, 的繼承層次比 的繼承層次低。

使用泛型的場景

調(diào)用一個(gè)方法:foo(src, dest);src 看做入?yún)ⅲ?b>dest 看做出參,基于以下規(guī)則決定是否使用和如何使用泛型:

入?yún)⑹褂蒙辖缤ㄅ浞?b>extends

出參使用下界通配符:super

入?yún)⒖梢杂?Object 代替的,使用無邊通配符

需要獲取入?yún)⒑统鰠⒌淖兞浚灰褂猛ㄅ浞?/p>

這種原則也叫做 PECS(Producer Extends Consumer Super) 原則。

類型擦除
類型擦除確保被參數(shù)化的類型不會創(chuàng)建新的類,不會產(chǎn)生運(yùn)行時(shí)的開銷。

泛型擦除時(shí),編譯器做了一點(diǎn)小小的工作:如果該泛型參數(shù)有邊界限制,替換成它的邊界;否則,用 Object 替換。
上述泛型類 Pair 會被替換成下面形式:

class Pair{
    Object first;
    Object last;
    public Object getFirst(){}
    public Object getLast(){}
}

P.S. 一般使用第一個(gè)限定類型替換變?yōu)樵碱愋停瑳]有限定類型,使用 Object 替換。

橋接方法

當(dāng)子類繼承(或?qū)崿F(xiàn))父類(或接口)的泛型方法時(shí),在子類中指明了具體的類型。編譯器會自動構(gòu)建橋接方法(bridge method)。如:

class Node{
    private T t;
    public Node(T t){
        setT(t);
    }
    public void setT(T t){
        this.t = t;
    }
}

class MyNode extends Node{
    public MyNode(Integer i){
        super(i);
    }
    public void setT(Integer i){
        super.setT(i);
    }
}

在上述代碼中,編譯時(shí)期,由于泛型擦除,Node 中的方法為 setT(Object t) 而 MyNode 中的方法為 setT(Inetger i) 。簽名不匹配,不再是重寫,因此,編譯器為 MyNode 生成如下橋接方法:

// 橋接方法
public void setT(Object i){
    setData((Integer)i);
}

public void setT(Integer i){
    super.setData(i);
}
非具體化類型 非具體化類型定義

具體類型(Reifiable Type)指的是:原始數(shù)據(jù)類型、非泛型類型、原生類型和調(diào)用不受限的通配等在運(yùn)行時(shí)期,信息不會丟失的類型。
非具體類型(Non-Reifiable Type)在運(yùn)行時(shí)期不能獲取其所有的信息,如 JVM 無法區(qū)別 ListList 。因此,這種類型不能使用類似 instanceof 的方法。

堆污染

堆污染指的是:一個(gè)參數(shù)化類型指向一個(gè)非該參數(shù)化類型對象的過程。通常是,在程序中進(jìn)行了一些操作,使編譯時(shí)期發(fā)生未檢查(unchecked)警告時(shí)發(fā)生。如:混用原始類型(Raw Type)和參數(shù)化類型。

使用非具體化類型做可變參數(shù)的潛在缺陷

當(dāng)使用可變參數(shù)作為泛型輸入?yún)?shù)時(shí),會造成堆污染。如:
可以通過如下注解消除編譯時(shí)期的警告:

@SafeVarargs

@SuppressWarnings({"unchecked", "varargs"})

泛型的限制

雖然泛型是如此的便利,但不免有缺點(diǎn):

不能用基本類型實(shí)例化類型參數(shù)

// 編譯出錯(cuò)
List array = new ArrayList<>();

不能通過類型參數(shù)實(shí)例化對象

public static  void foo(List list){
    // 編譯出錯(cuò)
    E element = new E();
    list.add(element);
}

不能創(chuàng)建泛型變量類型的靜態(tài)字段

public class Foo{
    // 編譯出錯(cuò)
    private static T field;
}

不能使用 instanceof 來確認(rèn)參數(shù)類型

public static  void foo(List list){
    // 編譯出錯(cuò)
    if(list instanceof ArrayList){
    }
}

不能創(chuàng)建參數(shù)化類型數(shù)組

// 編譯出錯(cuò)
List[] strings = new ArrayList<>[2];

不能拋出或捕獲泛型類實(shí)例

// 編譯出錯(cuò)
public class FooException extends Exception{
}

不能重載擦除后有同樣方法簽名的方法

public class Example{
    // 編譯出錯(cuò)
    public void print(Set string){
    }
    public void print(Set integer){
    }
}

運(yùn)行時(shí)類型查詢只適用于原始類型

Varargs 警告

泛型類的靜態(tài)上下文的類型變量無效

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/71063.html

相關(guān)文章

  • 基礎(chǔ)學(xué) Java】序

    摘要:本人生性愚鈍,在大學(xué)期間沒能好好領(lǐng)略等面向?qū)ο缶幊痰镊攘Α,F(xiàn)借助一些較為權(quán)威的書籍資料,將基礎(chǔ)知識里比較重要的東西整理成文,命名從基礎(chǔ)學(xué)。如果博文不慎侵犯了您的著作權(quán),請聯(lián)系我。 和很多大學(xué)一樣,我的學(xué)校也是從 Java 、C++ 入手,教給我們面向?qū)ο?(OOP) 的思想。本人生性愚鈍,在大學(xué)期間沒能好好領(lǐng)略 Java 等面向?qū)ο缶幊痰镊攘Α,F(xiàn)借助一些較為權(quán)威的書籍資料,將 Java...

    JackJiang 評論0 收藏0
  • 作為我的的第一門語言,學(xué)習(xí)Java時(shí)是什么感受?

    摘要:作為技術(shù)書籍或者視頻,講解一門語言的時(shí)候都是從最底層開始講解,底層的基礎(chǔ)有哪些呢首先是整個(gè),讓我們對這門語言先混個(gè)臉熟,知道程序的基本結(jié)構(gòu),順帶著還會說一下注釋是什么樣子。 2018年新年剛過,就迷茫了,Java學(xué)不下去了,不知道從哪里學(xué)了。 那么多細(xì)節(jié)的東西,我根本記不住,看完就忘。 剛開始學(xué)習(xí)的時(shí)候熱情萬丈,持續(xù)不了幾天就慢慢退去。 作為技術(shù)書籍或者視頻,講解一門語言的時(shí)候都是...

    isaced 評論0 收藏0
  • java篇 - 收藏集 - 掘金

    摘要:進(jìn)階多線程開發(fā)關(guān)鍵技術(shù)后端掘金原創(chuàng)文章,轉(zhuǎn)載請務(wù)必將下面這段話置于文章開頭處保留超鏈接。關(guān)于中間件入門教程后端掘金前言中間件 Java 開發(fā)人員最常犯的 10 個(gè)錯(cuò)誤 - 后端 - 掘金一 、把數(shù)組轉(zhuǎn)成ArrayList 為了將數(shù)組轉(zhuǎn)換為ArrayList,開發(fā)者經(jīng)常... Java 9 中的 9 個(gè)新特性 - 后端 - 掘金Java 8 發(fā)布三年多之后,即將快到2017年7月下一個(gè)版...

    OpenDigg 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<