第十五章 泛型

泛型的引入的原因:主要解決容器類存放數(shù)據(jù)的靈活性.
泛型的主要目的之一:用來制定容器類可以存放什么類型對象,而且有編譯器來保證類型的正確性.[說明在編譯時就能確定]核心概念:告訴編譯器想使用什么類型,然后編譯器幫你處理一切細(xì)節(jié).

15.2.1 用泛型返回多個對象(一個元組類庫)

package tinking_in_java.generics;
/**
 * Created by leon on 17-12-18.
 */
public class Holder<T> {
    private T t;
    public Holder(T t) {
        this.t = t;
    }
    public void setT(T t) {
        this.t = t;
    }
    public T getT() {
        return t;
    }

package tinking_in_java.generics;
/**
 * Created by leon on 17-12-18.
 */
public class Tuple<A, B> {
    public final A a;
    public final B b;
    public Tuple(A a, B b) {
        this.a = a;
        this.b = b;
    }
    @Override
    public String toString() {
        return "" + a + b;
    }
}
/**
 * 三元元組
 *
 * @param <A>
 * @param <B>
 * @param <C>
 */
class ThreeTuple<A, B, C> extends Tuple<A, B> {
    public final C c;
    public ThreeTuple(A a, B b, C c) {
        super(a, b);
        this.c = c;
    }
    @Override
    public String toString() {
        return "A= " + a + " B= " + b + " C= " + c;
    }
}
class FourTuple<A, B, C, D> extends ThreeTuple<A, B, C> {
    public final D d;
    public FourTuple(A a, B b, C c, D d) {
        super(a, b, c);
        this.d = d;
    }
}

15.2.2泛型作為 LinkedStack<T>

package tinking_in_java.generics;
/**
 * Created by leon on 17-12-18.
 */
public class LinkedStack<T> {
    class Node<U> {
        private U value;
        private Node<U> next;
        public Node() {
            value = null;
            next = null;
        }
        public Node(U u, Node<U> nextNode) {
            value = u;
            next = nextNode;
        }
        public boolean isEnd() {
            return value == null && next == null;
        }
    }
    private Node<T> top = new Node<T>();
    public void push(T t) {
        top = new Node<>(t, top);
    }
    public T pop() {
        if (top.isEnd()) return null;
        T result = top.value;
        top = top.next;
        return result;
    }
    public static void main(String[] args) {
        LinkedStack<String> myStack = new LinkedStack<>();
        myStack.push("abc");
        myStack.push("skjhklh");
        myStack.push("asdadf");
        String myStr = null;
        while ((myStr = myStack.pop()) != null) {
            System.out.println(myStr);
        }
    }
}

15.4泛型方法

泛型方法和是否是泛型類沒有關(guān)系.以下是一條基本原則: 無論何時,只要你能做到,盡量使用泛型方法,也就是說如果泛型方法能代替泛型類,就應(yīng)該采用泛型方法,因為他可以把事情更清楚明白.static方法無法使用泛型類的類型參數(shù),如果需要使static 方法使用泛型參數(shù)必須申明為泛型方法.

泛型方法定義:在返回值前加<T> .
杠桿利用,類型參數(shù)推斷.
可以做一個泛型推斷生成器工具類.(但是這只對賦值操作有效,如果把他直接傳入函數(shù)參數(shù)時是無效的,傳入?yún)?shù)時候,編譯器認(rèn)為執(zhí)行泛型方法后,返回值賦給一個Object類型變量)

package tinking_in_java.generics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
 * Created by leon on 17-12-18.
 */
public class NewUtils {
    public static <V, K> HashMap<V, K> map() {
        return new HashMap<>();
    }
    public static <T> List<T> list() {
        return new ArrayList<T>();
    }
    public static <T> LinkedList<T> linkedList() {
        return new LinkedList<T>();
    }
    public static <T> Queue<T> quene() {
        return new LinkedList<>();
    }
  public static void main(String[] args) {
    HashMap<String, List<String>> myHash = NewUtils.map();
  }

}

可變參數(shù)與泛型方法

泛型方法可以與可變參數(shù)很好的共存

public static <T> List<T> makeList(T... args) {
    ArrayList<T> list = new ArrayList<T>();
    for (T arg : args) {
        list.add(arg);
    }
    return list;
}
…
public static void main(String[] args) {
  List<String> makeList = makeList("a", "abc", "adalkj");
    System.out.println(makeList.toString());
}

15.5泛型構(gòu)建匿名內(nèi)部類
15.6 構(gòu)建復(fù)雜模型

15.7擦除的神秘.

在泛型代碼內(nèi)部,無法獲取獲取任何泛型參數(shù)類型信息.

因此List<String> ,List<Integer>在運行時事實上是相同類型的,這兩種形式都被摖除成”原生”類型,即List.所以為了確定泛型的參數(shù)類型,所以必須使用限定邊界.

邊界 <T extends AAA>,這個邊界說明 T 必須具有AAA,或者T是從AAA導(dǎo)出的類型(AAA是T的基類).泛型類型將摖除到他的第一個邊界(他可能會有多個邊界).

class Manipupolor<T extends Frob>
   private T obj;
    public Manipupolor(T t) {
        obj = t;
    }
    public void manipuplor() {
        obj.f();
    }
}

這摖除之后相當(dāng)于

class Manipupolor {
  private Frob obj;
    public Manipupolor(Frob t) {
        obj = t;
    }
    public void manipuplor() {
        obj.f();
    }
}

只有需要希望使用這個類型比某個具體類型(以及他所有子類)更加”泛化”的時候,也就是他能跨多個類工作時,泛型才有幫助.
泛型類型只有在靜態(tài)類型檢測期間才出現(xiàn),在此之后,程序中的所有泛型類型都會被摖除,替換成他們的非泛型上界.諸如List<T> ==>摖除成 List,而普通類型變量在未指定邊界情況下將摖除成Object. java采用摖除的原因是,要使得泛型能向后兼容.必須采取折中方案.
因為泛型擦除在方法體中移除了類型信息,所有在運行時的問題就是邊界:對象進入和離開的方法地點.

通過泛型創(chuàng)建類型實例.java中要創(chuàng)建通用實例,是做不到的,只能通過工廠方法預(yù)先針對不同類型創(chuàng)建.

15.8.2泛型數(shù)組

不能直接創(chuàng)建泛型數(shù)組(例如 new T[]),一般的解決方法是用arrayList<T> 創(chuàng)建,或者創(chuàng)建(T[])new Object[],(創(chuàng)建object數(shù)組,然后轉(zhuǎn)型T[]).因為有了擦除,數(shù)組在運行時只能是Object[],那么在編譯的時候如果強行轉(zhuǎn)成T[], 編譯期該數(shù)組的實際類型將會丟失,那么編譯器會錯過錯誤檢查,所以最好在集合內(nèi)部使用Object[],在使用時候再進行轉(zhuǎn)型T.其實ArrayList 就是內(nèi)部的存儲就是這么做得.

15.9邊界

extends關(guān)鍵字在泛型上下文環(huán)境中和普通情況下的意義完全不同.
1.通過在<T extends xxx> 來確定邊界
2.還可以在 class< > extends 基類來確定邊界.(好處是可以省去重復(fù)代碼)
例如:
第一種 :
BasicBound.java

package tinking_in_java.generics;
import java.awt.Color;
/**
 * Created by leon on 17-12-19.
 */
interface HashColor {
    Color getColor();
}
class Colored<T extends HashColor> {
    T item;
    public Colored(T t) {
        this.item = t;
    }
    T getItem() {
        return item;
    }
    public Color color() {
        return item.getColor();
    }
}
class Dimension {
    public int x, y, z;
}
//extends 多重繼承關(guān)系 ,必須class 在前,interface在后
class DimensionColor<T extends Dimension & HashColor> {
    T item;
    public DimensionColor(T t) {
        item = t;
    }
    public Color color() {
        return item.getColor();
    }
    public int getX() {
        return item.x;
    }
    public int getY() {
        return item.y;
    }
    public int getZ() {
        return item.z;
    }
}
interface Weight {
    int getWeight();
}
class Solid<T extends Dimension & HashColor & Weight> {
    T item;
    public Solid(T t) {
        item = t;
    }
    T getItem() {
        return item;
    }
    Color color() {
        return item.getColor();
    }
    int getX() {
        return item.x;
    }
    int getY() {
        return item.y;
    }
    int getZ() {
        return item.z;
    }
    int weight() {
        return item.getWeight();
    }
}
class Bound extends Dimension implements HashColor, Weight {
    @Override
    public Color getColor() {
        return Color.RED;
    }
    @Override
    public int getWeight() {
        return 0;
    }
}
public class BasicBound {
    public static void main(String[] args) {
        Solid<Bound> solid = new Solid<>(new Bound());
        System.out.println("" + solid.getX() + solid.getY() + solid.color() + solid.weight());
    }
}

第二種,采用繼承基類的形式:

package tinking_in_java.generics;
import java.awt.Color;
/**
 * Created by leon on 17-12-19.
 */
class Hold<T> {
    T item;
    Hold(T t) {
        item = t;
    }
    T getItem() {
        return item;
    }
}
class Colors2<T extends HashColor> extends Hold<T> {
    Colors2(T t) {
        super(t);
    }
    Color color() {
        return item.getColor();
    }
}
class DimensionColor2<T extends Dimension & HashColor> extends Colors2<T> {
    DimensionColor2(T t) {
        super(t);
    }
    int getX() {
        return item.x;
    }
    int getY() {
        return item.y;
    }
    int getZ() {
        return item.z;
    }
}
class Solid2<T extends Dimension & HashColor & Weight> extends DimensionColor2<T> {
    Solid2(T t) {
        super(t);
    }
    int weight() {return item.getWeight();   }
}
public class InheritBound {
    public static void main(String[] args) {
        Solid2<Bound> solid2 = new Solid2<>(new Bound());
        System.out.println("" + solid2.getX() + solid2.getY() + solid2.color() + solid2.weight());
    }
}

15.10通配符類型:

協(xié)變<? Extends MyClass> :具有任何從MyClass 繼承類的通配符 Pair<? extends Emplee> myPair=new Pair<Mannager>(Bob,Linar) .只可以讀取數(shù)據(jù),不能通過myPair 往里面再次添加數(shù)據(jù).

15.10.2 逆變(使用超類通配符)

<? Super MyClass>甚至可以用<? Super T> 有某個特定類任一的基類來界定.
解讀協(xié)變和逆變:

引入的原因是解決 單一泛型類型的制約.

對于協(xié)變List<? extends Number > numList;

image.png

從語義上分析:
修改:因為? 都是繼承Number ,所以按理說 既可以往numList 添加 Integer,也可以往里面添加Float,也可以添加Double,Long.但是這樣就會出現(xiàn)問題,編譯器無法知道究竟往里面添加了什么類型的數(shù)據(jù).[因此為了保證明確性,規(guī)定不能這樣做]
讀取:因為都是繼承Number ,所以這里面的數(shù)據(jù)必然能讀取到Number,因為里面的數(shù)據(jù)要么是Number,要么是Number的子類數(shù)據(jù).[所以允許讀取]

對于逆變List<? Super Integer>

image.png

讀取 :按照字面意思 因為容器里面 的數(shù)據(jù)都是 Integer的超類,那么:既可以讀取Integer,也可以讀取Number,還可以讀取Object .這就造成讀取數(shù)據(jù)的不確定性[所以禁止讀取]
修改 :為了保證數(shù)據(jù)的確定性,必須往里面寫入Integer 或者Integer的子類(因為Integer的子類也屬于Integer).

適用場景:生產(chǎn)者(獲取數(shù)據(jù))適用Extends ,消費者(裝入數(shù)據(jù))適用 Super.

例如:

// Collections.java
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());
        }
    }
}

包含泛型相同名字,參數(shù)類型相似的多個接口不能同時被一個類繼承,因為擦除的作用,他們會被當(dāng)做相同的接口.一個類中如果傳有多個泛型T,V ,不能把不同泛型當(dāng)做不同類型數(shù)據(jù)看待進行函數(shù)重載,因為擦除之后他們的類型都是會變成Object.

15.12自限定的類型.

class SelfBounded<T extends SelfBounded<T>>{…}

這就話的意思是 "SelfBounded類接受泛型參數(shù)T,而T由一個邊界類限定,這個邊界就是擁有T作為其參數(shù)的SelfBounded".這個的好處在與,能使得 導(dǎo)出類用自己作為參數(shù)以及 返回類型.但是這在編譯器中并不是強制要求這么做的,一般來說需要要求其他每個用這種方式的人遵循這個原則.

package tinking_in_java.generics;
/**
 * Created by leon on 17-12-19.
 */
/**
 * 自限定 要求繼承者都必須傳入?yún)?shù)和 返回參數(shù)有相同
 *
 * @param <T>
 */
class SelfBounded<T extends SelfBounded<T>> {
    T elment;
    SelfBounded<T> set(T t) {
        elment = t;
        return this;
    }
    public T get() {
        return elment;
    }
}
/**
 * 屬于正確理解
 */
class A extends SelfBounded<A> {
}
/**
 * 不屬于正確理解,但是編譯器不會報錯
 */
class B extends SelfBounded<A> {
}
/**
 * 屬于正確理解,參數(shù)和返回參數(shù)都是C類型
 */
class C extends SelfBounded<C> {
    C setAndGet(C c) {
        set(c);
        return get();
    }
}
public class SelfBounding {
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,627評論 2 380

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

  • 泛型實現(xiàn)了參數(shù)化類型的概念。 簡單泛型 容器是出現(xiàn)泛型的重要原因之一。泛型的主要目的之一就是用來指定容器要持有什么...
    MAXPUP閱讀 535評論 0 0
  • 在之前的文章中分析過了多態(tài),可以知道多態(tài)本身是一種泛化機制,它通過基類或者接口來設(shè)計,使程序擁有一定的靈活性,但是...
    _小二_閱讀 696評論 0 0
  • 開發(fā)人員在使用泛型的時候,很容易根據(jù)自己的直覺而犯一些錯誤。比如一個方法如果接收List作為形式參數(shù),那么如果嘗試...
    時待吾閱讀 1,072評論 0 3
  • When I was sane, often in his thoughts on the night peopl...
    深夜閱書閱讀 231評論 0 0
  • 江湖曾有傳言,說我是一枚大規(guī)模殺傷性武器。 這話根本就不靠譜,試想,若我真屬于此類范疇的話,聯(lián)合國豈能容我活到今天...
    唐家小癡閱讀 1,009評論 34 27