26.raw タイプは使ってはならない
- rawタイプとは、型パラメータなしで宣言されたジェネリック型のことを指す。例えば、List< E > に対する、List のようなものをrawタイプと呼ぶ。
- rawタイプを用いると、型パラメータを明確にして宣言したジェネリクス型であればコンパイル時に検出できた誤りが、実行時まで検出されなくなる。
package tryAny.effectiveJava;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class GenericsTest1 {
public static void main(String[] args) {
final Collection stamps = Arrays.asList(new Stamp(), new Stamp(), "");
// 以下のコードだとコンパイルエラーとして検出してくれる。
// final Collection<Stamp> stamps = Arrays.asList(new Stamp(), new Stamp(), "");
for (Iterator i = stamps.iterator(); i.hasNext();) {
Stamp stamp = (Stamp) i.next(); // 3つ目の要素をキャストするときにエラー送出。実行時に初めてわかる。
System.out.println("cast success");
}
}
static class Stamp {
}
}
- Listのようなrawタイプを使うべきではないが、任意の型を許容する、List< Object > のようなジェネリック型は使用してもよい。
大まかに、この2つの違いは、前者はジェネリックに適応していないが、後者はコンパイラに明確にどのような型も許容すると明確に示している。
例えば、List< String >をList型に入れることはできるが、List< Object >に入れることはできない。
package tryAny.effectiveJava;
import java.util.ArrayList;
import java.util.List;
public class GenericsTest2 {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
unsafeAdd(strings, Integer.valueOf(42));
String s = strings.get(0); // Has compiler-generated cast
}
private static void unsafeAdd(List list, Object o) {
// 以下のコードにするとコンパイルエラーにしてくれる。
// private static void unsafeAdd(List<Object> list, Object o) {
list.add(o);
}
}
- 要素となる型が何であれ構わない場合にはrawタイプを使いたくなるかもしれないが、**?**のワイルドカードを使うべきである。
これらの違いも型安全性にある。
package tryAny.effectiveJava;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GenericsTest3 {
public static void main(String[] args) {
Set<?> s1 = Stream.of("2", "b", 1).collect(Collectors.toSet());
Set s2 = Stream.of("2", "c", 1).collect(Collectors.toSet());
// s1.add("c"); // このコードはコンパイルエラーとなる
s2.add("b");
System.out.println(numElementsInCommon(s1, s2));
}
// Use of raw type for unknown element type - don't do this!
static int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o1 : s1)
if (s2.contains(o1))
result++;
return result;
}
}
- rawタイプを使うべきでないという規則の例外が少数ある。
- クラスのリテラルに型パラメータを使ったジェネリック型を指定することはできない。つまり、List.classやString[].classは許されるが、List< String >やList< ? > は許されない。
- instanceof を型パラメータを使ったジェネリック型に対して使うことはできないので(Unbounded wildcard type除く)、その時はrawタイプで検査する。
// Legitimate use of raw type - instanceof operator
if (o instanceof Set) { // Raw type
Set<?> s = (Set<?>) o; // Wildcard type
...
}