要有效地使用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) { } }
重载将共享相同的类文件表示,并将生成编译时错误。