博客地址:Java泛型:類型擦除
前情回顧Java泛型:泛型類、泛型接口和泛型方法
類型擦除 代碼片段一Class c1 = new ArrayList().getClass(); Class c2 = new ArrayList ().getClass(); System.out.println(c1 == c2); /* Output true */
顯然在平時使用中,ArrayList
這就是Java泛型的類型擦除造成的,因為不管是ArrayList
在編譯器層面做的這件事(擦除具體的類型信息),使得Java的泛型先天都存在一個讓人非常難受的缺點:
代碼片段二在泛型代碼內部,無法獲得任何有關泛型參數類型的信息。
Listlist = new ArrayList (); Map map = new HashMap (); System.out.println(Arrays.toString(list.getClass().getTypeParameters())); System.out.println(Arrays.toString(map.getClass().getTypeParameters())); /* Output [E] [K, V] */
關于getTypeParameters()的解釋:
Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order. Returns an array of length 0 if the underlying generic declaration declares no type variables.
我們期待的是得到泛型參數的類型,但是實際上我們只得到了一堆占位符。
代碼片段三public class Main{ public T[] makeArray() { // error: Type parameter "T" cannot be instantiated directly return new T[5]; } }
我們無法在泛型內部創建一個T類型的數組,原因也和之前一樣,T僅僅是個占位符,并沒有真實的類型信息,實際上,除了new表達式之外,instanceof操作和轉型(會收到警告)在泛型內部都是無法使用的,而造成這個的原因就是之前講過的編譯器對類型信息進行了擦除。
同時,面對泛型內部形如T var;的代碼時,記得多念幾遍:它只是個Object,它只是個Object……
代碼片段四public class Main{ private T t; public void set(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Main m = new Main (); m.set("findingsea"); String s = m.get(); System.out.println(s); } } /* Output findingsea */
雖然有類型擦除的存在,使得編譯器在泛型內部其實完全無法知道有關T的任何信息,但是編譯器可以保證重要的一點:內部一致性,也是我們放進去的是什么類型的對象,取出來還是相同類型的對象,這一點讓Java的泛型起碼還是有用武之地的。
代碼片段四展現就是編譯器確保了我們放在t上的類型的確是T(即便它并不知道有關T的任何類型信息)。這種確保其實做了兩步工作:
set()處的類型檢驗
get()處的類型轉換
這兩步工作也成為邊界動作。
代碼片段五public class Main{ public List fillList(T t, int size) { List list = new ArrayList (); for (int i = 0; i < size; i++) { list.add(t); } return list; } public static void main(String[] args) { Main m = new Main (); List list = m.fillList("findingsea", 5); System.out.println(list.toString()); } } /* Output [findingsea, findingsea, findingsea, findingsea, findingsea] */
代碼片段五同樣展示的是泛型的內部一致性。
擦除的補償如上看到的,但凡是涉及到確切類型信息的操作,在泛型內部都是無法共工作的。那是否有辦法繞過這個問題來編程,答案就是顯示地傳遞類型標簽。
代碼片段六public class Main{ public T create(Class type) { try { return type.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { Main m = new Main (); String s = m.create(String.class); } }
代碼片段六展示了一種用類型標簽生成新對象的方法,但是這個辦法很脆弱,因為這種辦法要求對應的類型必須有默認構造函數,遇到Integer類型的時候就失敗了,而且這個錯誤還不能在編譯器捕獲。
進階的方法可以用限制類型的顯示工廠和模板方法設計模式來改進這個問題,具體可以參見《Java編程思想 (第4版)》P382。
代碼片段七public class Main{ public T[] create(Class type) { return (T[]) Array.newInstance(type, 10); } public static void main(String[] args) { Main m = new Main (); String[] strings = m.create(String.class); } }
代碼片段七展示了對泛型數組的擦除補償,本質方法還是通過顯示地傳遞類型標簽,通過Array.newInstance(type, size)來生成數組,同時也是最為推薦的在泛型內部生成數組的方法。
以上,泛型的第二部分的結束。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64658.html
摘要:然而中的泛型使用了類型擦除,所以只是偽泛型。總結本文介紹了泛型的使用,以及類型擦除相關的問題。一般情況下泛型的使用比較簡單,但是某些情況下,尤其是自己編寫使用泛型的類或者方法時要注意類型擦除的問題。 簡介 Java 在 1.5 引入了泛型機制,泛型本質是參數化類型,也就是說變量的類型是一個參數,在使用時再指定為具體類型。泛型可以用于類、接口、方法,通過使用泛型可以使代碼更簡單、安全。然...
摘要:可以看到,如果我們給泛型類制定了上限,泛型擦除之后就會被替換成類型的上限。相應的,泛型類中定義的方法的類型也是如此。參考語言類型擦除下界通配符和的區別 本篇博客主要介紹了Java類型擦除的定義,詳細的介紹了類型擦除在Java中所出現的場景。 1. 什么是類型擦除 為了讓你們快速的對類型擦除有一個印象,首先舉一個很簡單也很經典的例子。 // 指定泛型為String List list1 ...
摘要:靜態變量是被泛型類的所有實例所共享的。所以引用能完成泛型類型的檢查。對于這個類型系統,有如下的一些規則相同類型參數的泛型類的關系取決于泛型類自身的繼承體系結構。事實上,泛型類擴展都不合法。 前言 和C++以模板來實現靜多態不同,Java基于運行時支持選擇了泛型,兩者的實現原理大相庭徑。C++可以支持基本類型作為模板參數,Java卻只能接受類作為泛型參數;Java可以在泛型類的方法中取得...
閱讀 892·2021-10-13 09:39
閱讀 1481·2021-10-11 10:57
閱讀 2589·2019-08-26 13:53
閱讀 2538·2019-08-26 12:23
閱讀 3680·2019-08-23 18:30
閱讀 3745·2019-08-23 18:08
閱讀 2524·2019-08-23 18:04
閱讀 2959·2019-08-23 16:28