无法使用基本类型实例化泛型

请考虑以下参数化类型:

  1. class Pair<K, V> {
  2. private K key;
  3. private V value;
  4. public Pair(K key, V value) {
  5. this.key = key;
  6. this.value = value;
  7. }
  8. }
  9. // 创建Pair对象时,不能将基本类型替换为类型参数K或V
  10. Pair<int, char> p = new Pair<>(8, 'a'); // compile-time error
  11. // 只能将非基本类型替换为类型参数K和V
  12. Pair<Integer, Character> p = new Pair<>(8, 'a');
  13. // Java编译器会自动装箱,将8转为Integer.valueOf(8),将'a'转为Character('a')
  14. Pair<Integer, Character> p;
  15. p = new Pair<>(Integer.valueOf(8), new Character('a'));

无法创建类型参数的实例

无法创建类型参数的实例。例如,以下代码导致编译时错误:

  1. // compile-time error
  2. public static <E> void append(List<E> list) {
  3. E elem = new E();
  4. list.add(elem);
  5. }
  6. // OK 通过反射创建类型参数的对象
  7. public static <E> void append(List<E> list, Class<E> cls) throws Exception {
  8. E elem = cls.newInstance();
  9. list.add(elem);
  10. }
  11. // 按如下方式调用append方法
  12. List<String> ls = new ArrayList<>();
  13. append(ls, String.class);

无法声明类型为类型参数的静态字段

类的静态字段是类的所有非静态对象共享的类级变量。因此,不允许使用类型参数的静态字段。考虑以下类:

  1. public class MobileDevice<T> {
  2. private static T os;
  3. // ...
  4. }

如果允许类型参数的静态字段,则以下代码将混淆:

  1. MobileDevice<Smartphone> phone = new MobileDevice<>();
  2. MobileDevice<Pager> pager = new MobileDevice<>();
  3. MobileDevice<TabletPC> pc = new MobileDevice<>();

因为静态字段os由phone、pager、pc所共享的,所以os的实际类型是什么呢?它不能同时是Smartphone、Pager或者TabletPC。因此,无法创建类型参数的静态字段。

无法使用具有参数化类型的强制转换或instanceof

因为Java编译器会擦除通用代码中的所有类型参数,所以无法验证在运行时使用泛型类型的参数化类型:

  1. public static <E> void rtti(List<E> list) {
  2. if (list instanceof ArrayList<Integer>) { // compile-time error
  3. // ...
  4. }
  5. }

传递给rtti方法的参数化类型集是:

  1. S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }

运行时不跟踪类型参数,因此无法区分ArrayList<Integer>ArrayList<String>。可以做的最多是使用无界通配符来验证列表是否为ArrayList:

  1. public static void rtti(List<?> list) {
  2. if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type
  3. // ...
  4. }
  5. }

通常,除非通过无界通配符对参数化进行参数化,否则无法强制转换为参数化类型。例如:

  1. List<Integer> li = new ArrayList<>();
  2. List<Number> ln = (List<Number>) li; // compile-time error

但是,在某些情况下,编译器知道类型参数始终有效并允许强制转换。例如:

  1. List<String> l1 = ...;
  2. ArrayList<String> l2 = (ArrayList<String>)l1; // OK

无法创建参数化类型的数组

无法创建参数化类型的数组。例如,以下代码无法编译:

  1. List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error

以下代码说明了将不同类型插入到数组中时会发生什么:

  1. Object[] strings = new String[2];
  2. strings[0] = "hi"; // OK
  3. strings[1] = 100; // An ArrayStoreException is thrown.

如果使用通用列表尝试相同的操作,则会出现问题:

  1. Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed
  2. stringLists[0] = new ArrayList<String>(); // OK
  3. stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown,
  4. // but the runtime can't detect it.

如果允许参数化列表数组,则前面的代码将无法抛出所需的ArrayStoreException。

无法创建、捕获或抛出参数化类型的对象

泛型类不能直接或间接扩展Throwable类。例如,以下类将无法编译:

  1. // Extends Throwable indirectly
  2. class MathException<T> extends Exception { /* ... */ } // compile-time error
  3. // Extends Throwable directly
  4. class QueueFullException<T> extends Throwable { /* ... */ // compile-time error

方法无法捕获类型参数的实例:

  1. public static <T extends Exception, J> void execute(List<J> jobs) {
  2. try {
  3. for (J job : jobs)
  4. // ...
  5. } catch (T e) { // compile-time error
  6. // ...
  7. }
  8. }

但是可以在throws子句中使用类型参数:

  1. class Parser<T extends Exception> {
  2. public void parse(File file) throws T { // OK
  3. // ...
  4. }
  5. }

无法重载每个重载的形式参数类型擦除到相同原始类型的方法

类不能有两个重载方法,因为它们在类型擦除后具有相同的签名。

  1. public class Example {
  2. public void print(Set<String> strSet) { }
  3. public void print(Set<Integer> intSet) { }
  4. }

上述例子将生成编译时错误。