[効果java]アイテム29|それならジェネリックタイプ


ジェネリッククラスをJENERICタイプに変更します。

  • 通常クラスをJENICタイプに変換するクラスは、クライアントに危害を及ぼさない.
  • [オブジェクトベースのスタック-JENERICの強力な候補者が切実に必要]
    
    public class ObjectStack {
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        public ObjectStack() {
            this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
        }
    
        public void push(Object e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public Object pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
    
            Object result = elements[--size];
            elements[size] = null; // 다 쓴 참조 해제
    
            return result;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, 2 * size + 1);
            }
        }
    }

  • 上記のコード状態では、スタックから取り出したオブジェクトを変換する必要があり、実行時エラーが発生する可能性があります.だから、ジェニーンリックタイプのクラスに変えたほうがいいです.

  • 通常クラスでJENICタイプクラスにするのは、タイプパラメータの追加が始まりです.通常、タイプ名としてEが使用されます.
  • [ジェニーンリックスタックに変更]
    
    public class GenericStack<E> {
        private E[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        public GenericStack() {
            this.elements = new E[DEFAULT_INITIAL_CAPACITY]; // 오류발생
        }
    
        public void push(E e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public E pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
    
            E result = elements[--size];
            elements[size] = null; // 다 쓴 참조 해제
    
            return result;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, 2 * size + 1);
            }
        }
    }
  • 以上のコードのジェネレータにエラーが発生しましたアイテムに記載されているように、実体化不可能なタイプで配列を作成することはできません.
  • 上記の問題を解決する方法は2つあります.
  • タイプパラメータエラーを解決する2つの方法


    1.E配列を作成し、JENIC配列に変換されたJENIC配列の生成を禁止する制約を迂回します.
    [オブジェクト配列の作成時に配列を変換]
    ...
    public void push(E e) {
            ensureCapacity();
            elements[size++] = e;
    }
    
    public GenericStack() {
        elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; // 경고 메시지 발생
    }
    
    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
    ...
    // 비검사 형변환 경고 메시지 발생
    Unchecked cast: 'java.lang.Object[]' to 'E[]
  • 以上のコードは、非チェック変換警告メッセージを発行します.
  • 上記のコードでは、Object配列がelementsフィールドに格納されると、クライアントに返されるか、他のメソッドに転送されません.
  • private方法によってアレイに格納される要素のタイプは、常にpushである.
  • 上記の2つの理由により、この非検証変換は確かに安全である.
  • 使用E非チェック変換警告の範囲を最小限に抑え、警告を非表示にします.
  • [非チェック形式のコピー警告を非表示]
    ...
    public void push(E e) {
            ensureCapacity();
            elements[size++] = e;
    }
    
    // 배열 elements는 push(E)로 넘어온 E인스턴스만 남는다.
    // 타입 안정성을 보장하지만 이 배열의 런타임 타입은 Object[] 이다.
    @SuppressWarnings("unchecked")
    public GenericStack() {
        elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; // 경고 메시지 발생
    }
    
    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
    ...
    // 비검사 형변환 경고 메시지 발생
    Unchecked cast: 'java.lang.Object[]' to 'E[]
  • ジェネレータは配列を生成する以外に機能しないため、ジェネレータ全体に警告を隠すことができる.
  • 2.@SuppressWarnings("unchecked")フィールドのタイプをelementsからE[]に変更する方法.
    [オブジェクト配列を保持しpop()を使用してシェイプに変換]
    // 비검사 경고를 적절히 숨긴다.
    public class GenericStack<E> {
        private Object[] elements;
    	...
        public GenericStack() {
            this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
        }
    
        public void push(E e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public E pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
    		// push에서 E 타입만 허용하므로 이 형변환은 안전하다.
            @SuppressWarnings("unchecked")
            E result = (E) elements[--size];
            elements[size] = null; // 다 쓴 참조 해제
    
            return result;
        }
    	...
    }

    2つの方法の長所と短所は、

  • 第1の方法は可読性がよく、コードが短い.また,配列を1回生成するだけである.ただし、Object[]Eではなく(配列のコンパイル時間タイプとランタイムタイプが異なるため)、臀部汚染が開始される.
  • 第2の方法は、アレイ要素を読み出すたびにアレイを作成することである.
  • 整理する


    実体化不可能なタイプを処理する過程でコンパイラの警告を迂回する矛盾が生じるが,JENNERICタイプが提供するタイプ安定性は極めて便利であるため,JENNERICを適切かつ積極的に使用する.
    [Reference]
    臀部汚染