參考
限制
- 無法和原始類型使用, 即類型參數不能是<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