首頁>技術>

flag:

泛型是什麼?使用泛型的好處是什麼?java中的泛型是如何工作的?關於型別擦除的原理?泛型中的萬用字元有哪些?有什麼區別和作用?自定義泛型類、泛型方法、泛型引數編寫一段泛型程式來實現LRU快取?1. 什麼是泛型

泛型指的是可以將型別作為引數進行傳遞,其本質上就是型別引數化。比如:我們平時定義一個方法的時候,常會指定要傳入一個具體類物件作為引數。而如果使用泛型,那麼這個具體傳入類的物件,就可以指定為某個型別,而不必指定具體的類。也就是我們將某個型別作為引數進行傳遞了。

//普通方法public void testValue(String s) {}//泛型方法public <T> void testValue(T t) {}
2.為什麼要有泛型關於使用泛型與使用 Object 有什麼區別?

​ 如果我們使用Object,就要將傳入的型別強制轉換成我們需要的型別,如果傳入的型別不匹配將會導致程式包ClassCastException異常。

泛型在集合中的使用為例:

public class genericity {    public static void main(String[] args) {        List list = new ArrayList();        list.add("aaa");        list.add("bbb");        list.add("ccc");        list.add("ddd");        // 不使用泛型可能會存在資料安全問題        list.add(111);        list.add(new Object());    }}    List<String> list2 = new ArrayList<String>();    list2.add(111); // 宣告為 String 型別時,賦值為 111 會報錯

為什麼要有泛型(Generic)?

解決元素儲存的安全性問題

解決獲取資料元素時,需要型別強轉的問題

圖解上述程式碼:

在以上例子中,我們可以總結出,使用泛型的一些好處:

它可以避免型別強制轉換,而引起的程式異常。

可以使程式碼更加簡潔規範。

是程式碼更加靈活,可定製型強。

最重要的一點: 解決了傳參型別的安全性問題。 在聲明瞭 List<String>後只有指定型別才可以進行傳參,實現型別安全**,並且相對的,如果取值也使用該泛型方式,則可以實現讀取出來的物件不需要強轉,達到快速便捷的目的。**

3. Java的泛型是如何工作的?

​ 泛型是透過型別擦除來實現的,編譯器在編譯時擦除了所有型別相關的資訊,所以在執行時不存在任何型別相關的資訊。

​ 當泛型被擦除後,他有兩種轉換方式,第一種是如果泛型沒有設定型別上限,那麼將泛型轉化成Object型別,第二種是如果設定了型別上限,那麼將泛型轉化成他的型別上限。

​ 例如 List<Object> 在執行時僅用一個List來表示。這樣做的目的,是確保能和Java 5之前的版本開發二進位制類庫進行相容。你無法在執行時訪問到型別引數,因為編譯器已經把泛型型別轉換成了原始型別。

 //未指定上限    public class Test1<T> {        T t;        public T getValue() {            return t;        }        public void setVale(T t) {            this.t = t;        }    }    //指定上限    public class Test2<T extends List> {        T t;        public T getT() {            return t;        }        public void setT(T t) {            this.t = t;        }    }    //透過反射呼叫獲取他們的屬性型別    @Test    public void testType1() {        Test1<String> test1 = new Test1<>();        test1.setVale("11111");        Class<? extends Test1> aClass = test1.getClass();        for (Field field : aClass.getDeclaredFields()) {            System.out.println("Test1屬性:" + field.getName() + "的型別為:" + field.getType().getName());        }        Test2 test2 = new Test2();        test2.setT(new ArrayList());        Class<? extends Test2> aClass2 = test2.getClass();        for (Field field : aClass2.getDeclaredFields()) {            System.out.println("test2屬性:" + field.getName() + "的型別為:" + field.getType().getName());        }    }

上面方法列印的結果:

Test1屬性:t的型別為:java.lang.ObjectTest1屬性:this$0的型別為:com.company.genericitytest2屬性:t的型別為:java.util.Listtest2屬性:this$0的型別為:com.company.genericity
4.泛型晉級使用(泛型與繼承的關係)

繼承關係

即設定泛型上限,傳入的泛型必須是Parent型別或者是他的子類

public <T extends Parent> void testType(T t) {}

依賴關係的使用

泛型間可以存在依賴關係,比如下面的C是繼承自E。即傳入的型別是E型別或者是E型別的子類

public <E, C extends E> void testDependys(E e, C c) {}
5. 泛型中的萬用字元有哪些?有什麼區別和作用?

當我們不知道或者不關心實際操作型別的時候我們可以使用無限萬用字元,當我們不指定或者不關心操作型別,但是又想進行一定範圍限制的時候,我們可以透過新增上限或下限來起到限制作用。

無限萬用字元

無限萬用字元表示的是未知型別,表示不關心或者不能確定實際操作的型別,一般配合容器類使用。

public void testV(List<?> list) {}

需要注意的是: 無限萬用字元只能讀的能力,沒有寫的能力。

public void testV(List<?> list) {      Object o = list.get(0);    //編譯器不允許該操作   // list.add("jaljal");}

上面的List<?>為無限萬用字元,他只能使用get()獲取元素,但不能使用add()方法新增元素。(即使修改元素也不被允許)

定義了上限,其只有讀的能力。此方式表示引數化的型別可能是所指定的型別,或者是此型別的子類。

//t1要麼是Test2,要麼是Test2的子類public void testC(Test1<? extends Test2> t1) {    Test2 value = t1.getValue();    System.out.println("testC中的:" + value.getT());}

定義了下限,有讀的能力以及部分寫的能力,子類可以寫入父類。此方式表示引數化的型別可能是指定的型別,或者是此型別的父類

//t1要麼是Test5,要麼是Test5的父類public void testB(Test1<? super Test5> t1) {    //子類代替父類    Test2 value = (Test2) t1.getValue();    System.out.println(value.getT());}

萬用字元不能用作返回值

如果返回值依賴型別引數,不能使用萬用字元作為返回值。可以使用型別引數返回方式:

public <T> T testA(T t, Test1<T> test1) {    System.out.println("這是傳入的T:" + t);    t = test1.t;    System.out.println("這是賦值後的T:" + t);    return t;}
要從泛型類取資料時,用extends;要往泛型類寫資料時,用super;既要取又要寫,就不用萬用字元(即extends與super都不用)。

泛型中只有萬用字元可以使用super關鍵字,型別引數不支援 這種寫法

6. 什麼時候使用萬用字元

什麼時候使用萬用字元

萬用字元形式和型別引數經常配合使用型別引數的形式都可以替代萬用字元的形式能用萬用字元的就用萬用字元,因為萬用字元形式上往往更為簡單、可讀性也更好。型別引數之間有依賴關係、返回值依賴型別引數或者需要寫操作,則只能用型別引數。

原始碼中對泛型的使用可以參考 Collections 類的一些方法:

public static <T extends Comparable<? super T>> void sort(List<T> list)​public static <T> void sort(List<T> list, Comparator<? super T> c)​public static <T> void copy(List<? super T> dest, List<? extends T> src)​public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
7. 編寫一段泛型程式來實現LRU快取?

​ 這裡使用 LinkedHashMap 來實現。

class LRUCacheLinkedHashMap<K,V> extends  LinkedHashMap<K,V>{​    private int cacheSize;​    public LR

作者|wushaopei|掘金

14
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Jetpack 架構元件之 Lifecycle (一)起源