2020-11-16
[Effective Java] Item 23: Don’t use raw types in new code
Raw type vs Generics 例子比較
Raw type
// My stamp collection. Contains only Stamp instances
private final Collection stamps = ... ;
// Compiles fine
stamps.add(new Coin());
for(Iterator i = stamps.iterator(); i.hasNext(); ){
Stamp s = (Stamp) i.next(); // Throws ClassCastException; Manual cast
...// Do something with the stamp
}
Generics
// Parameterized colleciton type - typesafe
private final Collection<Stamp> stamps = ... ;
// a compile time error
stamps.add(new Coin()); // add(Stamp) in Collection<Stamp> cannot be applied to (Coin) stamps.add(new Coin())
for(Iterator i = stamps.iterator(); i.hasNext(); ){
Stamp s = i.next(); // No cast necessary
...// Do something with the stamp
}
總結:
- 雖然你很難會遇到這種情況,把硬幣放到郵票集合裡面,但是你會容易把java.util.Date instance放入java.sql.Date的一個collection中。使用generics,可以保證型別的安全,在編譯時就報錯,不用等到runtime的時候。同時,在從集合中取出例項時,不需要進行manual cast,編譯器會根據集合定義的parameter type,自動為你生成。
- 雖然使用raw type也是合法的,但是不要用,因為會缺失type safety。現在還支援raw type,是因為在java 1.5之前,已經有太多的程式碼是基於raw type的,要做到migration compatibility,比如可以將一個parameterized type的例項傳給一個含有raw type的方法。
再看個例子
// Uses raw type (List) - fails at runtime
public static void main(String[] args){
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42)); // List<String> is a subtype of the raw type List
String s = strings.get(0); // Compiler-generated cast String type, Compiles fine; Runtime ClassCastException - cannot cast the result of strings.get(0) integer to a String
}
// 方法一:
private static void unsafeAdd(List list, Object o){
list.add(o); // Compiles fine, List can have instance of type Object; there is an unchecked warning here, since you ingore, you pay the price
// 方法二:
private static void unsafeAdd(List<Object> list, Object o){
list.add(o); // Compiler error, List<String> is not a subtype of List<Object>
}
幾個容易混淆的概念
Value | Comments | |
---|---|---|
List | raw type, opt out of generic type checking, unsafe | 不要使用,會有type safety issue |
List<Object> | list holding objects of any type, safe | |
List<?> | unbounded wildcard type, list of some type, list holding objects of some unknown type, safe | 在你不知道或者不care actual type parameter的時候使用;是type safety的。 1)you cannot put any element(other than null) into a Colleciton<?> 2) 你也不能假設,你從這個list裡面取出什麼型別的例項 |
如何使用unbounded wildcard type
// Unbounded wildcard type- typesafe and flexible
static int numElementsInCommon(Set<?> s1, Set<?> s2){
int result = 0;
for(Object o1 : s1){
if(s2.contains(o1))
result++;
return result;
}
}
兩個特例情況下可以使用raw type
- You must use raw types in class literals. e.g List.class instead of List<String>.class and List<?>.
- instanceof 操作
// legitimate use of raw type - instanceof operator
if(o instanceof Set) {
Set<?> m = (Set<?>) o; // once you've determined that o is a Set, you must cast it to the wildcard type, not raw type
一些常用的術語
Term | Example |
---|---|
Parameterized type | List<String> |
Actual type parameter | String |
Generic type | List<E> |
Formal type parameter | E |
Unbounded wildcard type | List<?> |
Raw type | List |
Bounded type parameter | <E extends Number> |
Recursive type bound | <T extends Comparable<T>> |
Bounded wildcard type | List<? extends Number> |
Generic method | static <E> List <E> asList(E[ ] a) |
Type token | string.class |