Help us understand the problem. What is going on with this article?

Effective Java 第3版 第5章ジェネリックス

Java中級者以上の必須本である、Effective Java 第3版に Kindle版が出たので、まとめる。

前:Effective Java 第3版 第4章クラスとインタフェース
次:Effective Java 第3版 第6章enumとアノテーション

用語

  • パラメータ化された型・・・List<String>
  • 実型パラメータ・・・String
  • ジェネリック型・・・List<E>
  • 仮型パラメータ・・・E
  • 非境界ワイルドカード型・・・List<?>
  • 原形・・・List
  • 境界型パラメータ・・・<E extends Number>
  • 再帰型境界・・・<T extends Comparable<T>>
  • 境界ワイルドカード型・・・List<? extends Number>
  • ジェネリックメソッド・・・static <E> List<E> asList(E[] a)
  • 型トークン・・・String.class

項目26 原型を使わない

  • 型パラメータを伴わないジェネリック型である、原型を使わない。(List等で<>の無いもの)
  • List<Object>を使うのは構わない。List(原型)には、List<String>を渡すことができるが、List<Object>には渡せないので安全。
  • Set<Object>は、任意の型のオブジェクトを含むことが可能なSetを表すパラメータ型(安全)。
  • Set<?>は、何らかの不明な型のオブジェクトを含むことが可能なSetを表すワイルドカード型(安全)。
原型の例
// NG
private final Collection stamps = ... ;
// OK
private final Collection<Stamp> stamps = ... ;

// NG
static int numElementsInCommon(Set s1, Set s2) { ... }
// OK
static int numElementsInCommon(Set<?> s1, Set<?> s2) { ... }

項目27 無検査警告を取り除く

  • 全ての無検査警告は、実行時のClassCastExceptionの可能性を表している。
  • 無検査警告を取り除くことができない場合、@SuppressWarnings("unchecked")アノテーションにより警告を抑制し、理由をコメントに残す。
@SuppressWarnings("unchecked")アノテーションによる警告を抑制
public <T> T[] toArray(T[] a) {
  if (a.length < size) {
    // T[] として渡されたのと同じ型の配列を生成するので、このキャストは正しい
    @SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elementData, size, a.getClass());
    return result;    
  }
  System.arraycopy(elements, 0,va, 0, size);
  if (a.length > size)
    a[size] = null;
  return a;
}

項目28 配列よりもリストを選ぶ

  • 配列は欠陥があり、ジェネリックスとうまく調和しない。new List<E>[]new List<String>[]new E[]はどれも許されていない。
  • 配列はコンパイル時の型安全を提供しないが、ジェネリックスは型安全である。

項目29 ジェネリック型を使う

ジェネリクス未使用
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        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);
        }
    }
}
  • ObjectEに変更する。
ジェネリクスへの書き換え例
public class Stack<E> {
    private E[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    @SuppressWarnings("unchecked")    // 警告を抑制する
    public Stack() {
        // new E[] はできないので、Object[] を E[] にキャストする(無検査警告が出る)
        this.elements = (E[]) new Object[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);
        }
    }
}

項目30 ジェネリックメソッドを使う

  • ジェネリックメソッドの、型パラメータを宣言する型パラメータのリスト<E>は、メソッド修飾子とメソッドの戻り値型の間に入る。
  • 境界ワイルドカード型を使うことでもっと柔軟にできる。
ジェネリックメソッド
    public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
        Set<E> result = new HashSet<>(s1);
        result.addAll(s2);
        return result;
    }

項目31 APIの柔軟性向上のために境界ワイルドカードを使う

境界ワイルドカードを使った例
    // Eプロデューサ(生産者)としてのパラメータのためのワイルドカード型
    public void pushAll(Iterable<? extends E> src) {
        for (E e : src) {
            push(e);
        }
    }

    // Eコンシューマ(消費者)としてのパラメータのためのワイルドカード型
    public void popAll(Collection<? super E> dat) {
        while (!isEmpty()) {
            dat.add(pop());
        }
    }
  • 戻り値型として、境界ワイルドカード型を使わない。
public static <E> Set<E> union(Set<? extends E> e1, Set<? extends E> e2)

項目32 ジェネリックスと可変長引数に注意して組み合わせる

  • 可変長引数メソッドと、ジェネリックスはJava5で同時に追加されたが、それらは協調しない。
  • ヒープ汚染は、パラメータ化された型の変数が、その型では無いオブジェクトを参照している場合に発生する。
  • ジェネリックの可変長配列パラメータに値を保存するのは安全では無い。
  • ジェネリック可変長パラメータは型安全ではないが許されている。
  • ジェネリック可変長パラメータを持つメソッドを書くなら、最初にそのメソッドが型安全になるようにする。使いやすいように、@SafeVaragsアノテーションを付ける。

項目33 型安全な異種コンテナを検討する

  • mapで、キーをClass、値をinstanceとして格納するコンテナ。
型安全な異種コンテナパターン
public class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();

    public <T> void putFavorite(Class<T> type, T instance) {
        favorites.put(Objects.requireNonNull(type), instance);
    }

    public <T> T getFavorite(Class<T> type) {
        return type.cast(favorites.get(type));
    }
}

// 呼び出し例
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0xcafebabe);
f.putFavorite(Class.class, Favorites.class);
String favoriteString = f.getFavorite(String.class);
int favoriteInteger = f.getFavorite(Integer.class);
Class<?> favoriteClass = f.getFavorite(Class.class);
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away