Effective Java—Generic(2)

10391 ワード

[アイテム28]配列ではなくリストを使用します。


最初の配列は「共変」(共変)です.例えば、SubSuperのサブタイプである場合、アレイSub[]はアレイSuper[]のサブタイプである.これを公変と呼ぶ.つまり一緒に変わる.
逆にジュネーブは変わらない.List<Sub>は、List<Super>の下位タイプでも上位タイプでもない.
単純な比較ではジェニーリックに問題があると考えられていますが、実際に問題があるのは配列です.StringタイプをLongタイプのリポジトリに入れることはできません.次のコードに示すように、配列はコードの実行時にのみエラーを表示しますが、リストではコンパイル時にエラーを確認できます.
Object[] objectArray = new Long[1];
// ArrayStoreException 발생
objectArray[0] = "Kimtaeng";

// 아예 컴파일 오류
List<Object> objectList = new ArrayList<Long>();
objectList.add("Kimtaeng");

配列は公変的に存在し、ジェニーリックは非公変的に存在する.E, List<E>, List<String> のようなタイプを非実体化タイプ(non-reflable type)と呼ぶ.静音は実体化されていないため、コンパイル時よりも実行時に情報のタイプが少ない.
アレイに変換する場合、ネストされたアレイの生成エラーが発生したり、「非チェック変換」の警告が発生したりした場合、アレイE[]ではなくコレクションリスト(List)を使用して解決できることが多い.
public class Chooser {
    private final Object[] choiceArray;
    
    public Chooser(Collection choices) {
        this.choiceArray = choices.toArray();
    }
    
    // 이 메서드를 사용하는 곳에서는 매번 형변환이 필요하다.
    // 형변환 오류의 가능성이 있다.
    public Object choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceArray[rnd.nextInt(choiceArray.length)];
    }
}
上記のコードはGenericを使用して次のように変更できます.
public class Chooser<T> {
    private final T[] choiceArray;

    public Chooser(Collection<T> choices) {
        // 오류 발생 incompatible types: java.lang.Object[] cannot be converted to T[]
        this.choiceArray = choices.toArray();
    }

    // choose 메소드는 동일하다.
}
次の図に示すように、コードを変更すると互換性のないタイプのエラーが解決されます.
// Object 배열을 T 배열로 형변환하면 된다.
this.choiceArray = (T[]) choices.toArray();
コンパイルエラーは消えますが、Unchecked Cast警告が表示されます.タイプパラメータTがどのようなタイプなのか分からないため、運転時においても変換が安全であることは保証されない.ジェニーリックは実行時にタイプ情報をクリアするので、どんなタイプなのか分かりません.Unchecked Castなどのチェックされていない変換警告を削除するには、配列ではなくリストを使用します.
class Chooser<T> {
    private final List<T> choiceList;

    public Chooser(Collection<T> choices) {
        this.choiceList = new ArrayList<>(choices);
    }

    public T choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceList.get(rnd.nextInt(choiceList.size()));
    }
}
整理すると、配列は公共で実体化され、ジェニーリックは不公正で、タイプ情報が消去されます.したがって、配列は実行時にタイプが安全であり、コンパイル時には安全ではありません.ジュネーブはその道に逆らって行った.