参考
限制
- 无法和原始类型使用, 即类型参数不能是<int>这些.
- 不能生成类型参数的实例, 即不能new T(), 因为编译之后参数T已经没有了.
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
正确做法, 使用反射
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
- 无法用类型参数T声明类的静态变量
public class MobileDevice<T> {
private static T os;
// ...
}
原因:
If static fields of type parameters were allowed, then the following code would be confused:
- MobileDevice<Smartphone> phone = new MobileDevice<>();
- MobileDevice<Pager> pager = new MobileDevice<>();
- MobileDevice<TabletPC> pc = new MobileDevice<>();
Because the static field os is shared by phone, pager, and pc, what is the actual type of os? It cannot be Smartphone, Pager, and TabletPC at the same time. You cannot, therefore, create static fields of type parameters.
如果可以用T声明静态, 变量, 那么在上述情况中, 这个静态变量的类型到底是什么呢? 因为静态变量属于类, 所以静态变量不可能同时是上述的三个类型, 所以无法实现这点.
- 无法instanceof 带类型参数的泛型类
比如说不能boolean b = list instanceof ArrayList<String>
, 因为编译之后ArrayList<String>
实际变成了ArrayList
, 根本没有新的类产生. 同理也没有ArrayList<String>.class
.
但是可以这样list instanceof ArrayList<?>
, 等同于list instanceof ArrayList
- 类型转换的问题
首先List<Number>
和List<Integer>
不是协变的, 所以后者无法转变为前者, 编译器不会允许这个操作.
但是这种操作确实允许的, 不过会给出警告:
List<String> stringList = new LinkedList<>(); // 这里只是用错误类型举个例子
ArrayList<String> stringArrayList = (ArrayList<String>) stringList;
- 无法生成泛型数组
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
原因:
The following code illustrates what happens when different types are inserted into an array:
Object[] strings = new String[2];
strings[0] = "hi"; // OK
strings[1] = 100; // An ArrayStoreException is thrown.
上面代码展示了, 当数组中插入不同类型元素时候发生的事情. 运行到strings[1]=100
的时候, 会有异常.
假设可以创建泛型数组, 那么会发生以下情况:
If you try the same thing with a generic list, there would be a problem:
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.
If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
runtime不能detect这个应该是和泛型类型擦除有关.
- 无法生成, 捕获, 或者抛出泛型类型的对象
A generic class cannot extend the Throwable class directly or indirectly. For example, the following classes will not compile:
// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ } // compile-time error
// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ // compile-time error
A method cannot catch an instance of a type parameter:
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
// ...
}
}
- Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type
A class cannot have two overloaded methods that will have the same signature after type erasure.
public class Example {
public void print(Set<String> strSet) { }
public void print(Set<Integer> intSet) { }
}
因为Set<String>和Set<Integer>在编译后, 都变成了Set, 所以不满足重载条件.
一些习题
https://docs.oracle.com/javase/tutorial/java/generics/QandE/generics-answers.html