類型擦除(type erasure)。 Java中的泛型基本上都是在編譯器這個層次來實現的。在生成的Java字節代碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉。這個過程就稱爲類型擦除。如在代碼中定義的List<Object>和List<String>等類型,在編譯以後都會變成List。JVM看到的只是List,而由泛型附加的類型信息對JVM來講是不可見的。Java編譯器會在編譯時儘量的發現可能出錯的地方,可是仍然沒法避免在運行時刻出現類型轉換異常的狀況。類型擦除也是Java的泛型實現方式與C++模板機制實現方式之間的重要區別。 java
不少泛型的奇怪特性都與這個類型擦除的存在有關,包括:
泛型類並無本身獨有的Class類對象。好比並不存在List<String>.class或是List<Integer>.class,而只有List.class。
靜態變量是被泛型類的全部實例所共享的。對於聲明爲MyClass<T>的類,訪問其中的靜態變量的方法仍然是 MyClass.myStaticVar。無論是經過new MyClass<String>仍是new MyClass<Integer>建立的對象,都是共享一個靜態變量。 數據結構
當泛型碰見重載: spa
public class GenericTypes { public static void method(List<String> list) { System.out.println("invoke method(List<String> list)"); } public static void method(List<Integer> list) { System.out.println("invoke method(List<Integer> list)"); } }
這段代碼是不能被編譯的,是由於參數List<Integer>和List<String>編譯以後都被擦除了,變成了同樣的原生類型List<E>,擦除動做致使這兩個方法的特徵簽名變得如出一轍。初步看來,沒法重載的緣由已經找到了,可是真的就是如此嗎?只能說,泛型擦除成相同的原生類型只是沒法重載的其中一部分緣由。 code
public class GenericTypes { public static String method(List<String> list) { System.out.println("invoke method(List<String> list)"); return ""; } public static int method(List<Integer> list) { System.out.println("invoke method(List<Integer> list)"); return 1; } public static void main(String[] args) { method(new ArrayList<String>()); method(new ArrayList<Integer>()); } }兩個method方法添加了不一樣的返回值,因爲這兩個返回值的加入,方法重載竟然成功了,即這段代碼能夠被編譯和執行了。這是咱們對Java語言中返回值不參與重載選擇的基本認知的挑戰嗎?
重載固然不是根據返回值來肯定的,之因此此次能編譯和執行成功,是由於兩個mehtod()方法加入了不一樣的返回值後才能共存在一個Class文件之中。Class文件方法表(method_info)的數據結構時曾經提到過,方法重載要求方法具有不一樣的特徵簽名,返回值並不包含在方法的特徵簽名之中,因此返回值不參與重載選擇,可是在Class文件格式之中,只要描述符不是徹底一致的兩個方法就能夠共存。也就是說兩個方法若是有相同的名稱和特徵簽名,但返回值不一樣,那它們也是能夠合法地共存於一個Class文件中的。
對象