2020-11-16

專業搬磚工發表於2020-11-20

[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
}

總結:

  1. 雖然你很難會遇到這種情況,把硬幣放到郵票集合裡面,但是你會容易把java.util.Date instance放入java.sql.Date的一個collection中。使用generics,可以保證型別的安全,在編譯時就報錯,不用等到runtime的時候。同時,在從集合中取出例項時,不需要進行manual cast,編譯器會根據集合定義的parameter type,自動為你生成。
  2. 雖然使用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>
}

幾個容易混淆的概念

 ValueComments
Listraw 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

  1. You must use raw types in class literals. e.g List.class instead of List<String>.class and List<?>.
  2. 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

一些常用的術語

TermExample
Parameterized typeList<String>
Actual type parameterString
Generic typeList<E>
Formal type parameterE
Unbounded wildcard typeList<?>
Raw typeList
Bounded type parameter<E extends Number>
Recursive type bound<T extends Comparable<T>>
Bounded wildcard typeList<? extends Number>
Generic methodstatic <E> List <E> asList(E[ ] a)
Type tokenstring.class