Java的泛型

Java的泛型(generics)

\quad 前面寫了一篇文章專門用來說HashMap的,確實HashMap在平時是很是經常使用的,可是其中所涉及的內容還遠遠不止這些,舉個例子:安全

HashMap<String,String> hashMap = new HashMap<>();
       hashMap.put(1,"");//Error
複製代碼

\quad首先咱們知道,定義了一個鍵值對類型都是String類型的HashMap,若是你往裏面丟入一個類型爲非String類型的鍵值對,那麼確定是會報錯的。可是若是把尖括號及裏面的內容去掉的話,程序是能夠經過編譯的。bash

HashMap hashMap = new HashMap<>();
       hashMap.put(1,"");//Pass
複製代碼

那麼你會問,何苦要去掉括號呢?我明確的知道鍵值對的類型不是很方便嘛。確實,明確類型以後,編譯器能夠幫咱們檢查出不少插入數據類型不正確的問題,可是要知道泛型是在JDK1.5纔出現的,那麼在那個沒有泛型的年代,是怎麼確保類型安全的呢?ide

1.在沒有泛型的年代,如何確保類型安全?

\quad要知道,大多數的語言在最開始的時候都是沒有泛型的例如C#,Go(如今都不支持),Java。就拿前面例子中的hashMap來講,在JDK1.5以前,能夠在其中放入任何類型的數值。可是問題來了,若是咱們想構造一個只能傳入<String,String>類型的HashMap要怎麼作呢?能夠用相似裝飾器模式來實現,見代碼:ui

public class MyStringHashMapDecorator {
    HashMap hashMap = new HashMap();

    public void put(String key , String value){
        hashMap.put(key,value);
    }

    public HashMap get(String key){
        return (HashMap) hashMap.get(key);
    }
}
複製代碼

\quad可是問題來了,對於茫茫可能發生的狀況,按照這樣的話,我豈不是須要每一種都列出對應的狀態。因此在JDK1.5中推出了泛型,簡化了這些麻煩的操做,可是Java爲了向後兼容性,因此這種泛型只是在編譯期間存在的,運行期間仍是會經過類型擦除(Type Erasure),去掉泛型的。這一點能夠經過看字節碼能夠看出: spa

\quad 那麼問題來了,ArrayList<Integer>與ArrayList<Object>是同一個類嘛?
問題的答案是:是也不是,爲何這麼說呢?前面提到,在運行期間通過類型擦除以後,他們都回歸爲ArrayList。說不是的緣由在於他們之間不能相互賦值:
同時注意一點,ArrayList<String>並非ArrayList<Object>的子類型,也是因爲泛型擦除:

public static void main(String[] args) {
       foo(new ArrayList<String>());//Error

    }
    public static void foo(ArrayList<Object> objects){
        //do something
    }
複製代碼

2.泛型的綁定

\quad 下面經過幾個例子來引出泛型的使用:
code

  • 1.extends -限定泛型必須是自己或者是其子類(上邊界)

\quad 好比如今要比較兩個參數的大小,可是參數的類型不肯定,那麼就能夠用到泛型的綁定了。
舉例說明:cdn

public static void main(String[] args) {
        System.out.println(max(1,2));//int
        System.out.println(max(1.0,2.0));//double
        System.out.println(max(1.0f,2.0f));//float
        System.out.println(max(1L,2L));//long
        System.out.println(max("abc","abd"));//long
    }

    private static  <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
複製代碼

結果以下:blog

2
2.0
2.0
2
abd

Process finished with exit code 0
複製代碼

可是在這就有疑問了:
\quad 1.爲何Comparable明明是個接口,卻要用extends關鍵字?
\quad 2.若是對兩種都實現了Comparable接口,可是對兩者類型不一樣的參數進行比較,會有什麼結果呢?

回答:
\quad 1.在泛型中,關鍵字extends後面既能夠跟類,也能夠跟接口。可是在實際意義上更接近因而綁定類型的自身或者子類型,因此用extends關鍵字更加接近。
\quad 2.見代碼: 繼承

在編譯期間就會被檢查出錯誤,可是有種辦法能夠繞過這種檢查:
可是運行一樣報錯:

究其緣由的話,能夠看字節碼:
INVOKEINTERFACE-調用接口方法,經過前面的錯誤提示能夠看到,在這裏多態調用了String的compareTo方法,因此只能傳入一個String類型的參數。這裏咱們傳入了一個int,在類加載的驗證階段,就會找出這個錯誤。

  • 2.super-要求泛型必須是自己或者其父類型(下邊界)
    舉例說明:(Cat繼承了Animal類)
public static void main(String[] args) {
//在ArrayList中指定了T的類型,第二個參數傳入的是T類型的父類型
        sort(new ArrayList<Cat>(),new AnimalComparator());
    }

    private static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }
}

class AnimalComparator implements Comparator<Animal> {
    @Override
    public int compare(Animal o1, Animal o2) {
        return 0;
    }
}
複製代碼
相關文章
相關標籤/搜索