要有效地使用Java泛型,必須考慮如下限制:segmentfault
instanceof
考慮如下參數化類型:數組
class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } // ... }
建立Pair
對象時,不能將基本類型替換爲類型參數K
或V
:app
Pair<int, char> p = new Pair<>(8, 'a'); // compile-time error
你只能將非基本類型替換爲類型參數K
和V
:ui
Pair<Integer, Character> p = new Pair<>(8, 'a');
請注意,Java編譯器將8
自動裝箱到Integer.valueOf(8)
,將'a'
自動裝箱到Character('a')
:this
Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));
有關自動裝箱的詳細信息,請參閱自動裝箱和拆箱。code
你沒法建立類型參數的實例,例如,如下代碼致使編譯時錯誤:對象
public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); }
做爲解決方法,你能夠經過反射建立類型參數的對象:get
public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); }
你能夠按以下方式調用append
方法:編譯器
List<String> ls = new ArrayList<>(); append(ls, String.class);
類的靜態字段是類的全部非靜態對象共享的類級變量,所以,類型參數的靜態字段是不容許的,考慮如下類:string
public class MobileDevice<T> { private static T os; // ... }
若是容許類型參數的靜態字段,則如下代碼將混淆:
MobileDevice<Smartphone> phone = new MobileDevice<>(); MobileDevice<Pager> pager = new MobileDevice<>(); MobileDevice<TabletPC> pc = new MobileDevice<>();
由於靜態字段os
是由phone
、pager
和pc
共享的,因此os
的實際類型是什麼?它不能同時是Smartphone
、Pager
和TabletPC
,所以,你沒法建立類型參數的靜態字段。
instanceof
由於Java編譯器會擦除泛型代碼中的全部類型參數,因此沒法驗證在運行時使用泛型類型的參數化類型:
public static <E> void rtti(List<E> list) { if (list instanceof ArrayList<Integer>) { // compile-time error // ... } }
傳遞給rtti
方法的參數化類型集是:
S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }
運行時不跟蹤類型參數,所以它沒法區分ArrayList<Integer>
和ArrayList<String>
之間的區別,你能夠作的最可能是使用無界通配符來驗證列表是否爲ArrayList
:
public static void rtti(List<?> list) { if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type // ... } }
一般,除非經過無界通配符對其進行參數化,不然沒法強制轉換爲參數化類型,例如:
List<Integer> li = new ArrayList<>(); List<Number> ln = (List<Number>) li; // compile-time error
可是,在某些狀況下,編譯器知道類型參數始終有效並容許強制轉換,例如:
List<String> l1 = ...; ArrayList<String> l2 = (ArrayList<String>)l1; // OK
你沒法建立參數化類型的數組,例如,如下代碼沒法編譯:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
如下代碼說明了將不一樣類型插入到數組中時會發生什麼:
Object[] strings = new String[2]; strings[0] = "hi"; // OK strings[1] = 100; // An ArrayStoreException is thrown.
若是你使用泛型列表嘗試相同的操做,則會出現問題:
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed stringLists[0] = new ArrayList<String>(); // OK stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown, // but the runtime can't detect it.
若是容許參數化列表數組,則前面的代碼將沒法拋出所需的ArrayStoreException
。
泛型類不能直接或間接擴展Throwable
類,例如,如下類將沒法編譯:
// Extends Throwable indirectly class MathException<T> extends Exception { /* ... */ } // compile-time error // Extends Throwable directly class QueueFullException<T> extends Throwable { /* ... */ // compile-time error
方法沒法捕獲類型參數的實例:
public static <T extends Exception, J> void execute(List<J> jobs) { try { for (J job : jobs) // ... } catch (T e) { // compile-time error // ... } }
可是,你能夠在throws
子句中使用類型參數:
class Parser<T extends Exception> { public void parse(File file) throws T { // OK // ... } }
一個類不能有兩個在類型擦除後具備相同的簽名的重載方法。
public class Example { public void print(Set<String> strSet) { } public void print(Set<Integer> intSet) { } }
重載將共享相同的類文件表示,並將生成編譯時錯誤。