Upload
appresso-engineering-team
View
356
Download
6
Embed Size (px)
Citation preview
Effective Java 輪読会
2013/01/08
開発部田中
第5章 ジェネリックス(項目26~29)
• 項目26
– ジェネリック型を使用する
ジェネリック化
• Stackクラス– 互換性のために要素の型をObjectにしている
• 要素を取り出す際、クライアントでキャストが必要
• Object型を仮型パラメータEで置き換えることで、クラスをジェネリック化(一般化)する
• ジェネリック化により、コンパイル時に型の検査を行う
ジェネリック配列生成の禁止
• Stackクラスでコンパイルエラーが発生
– 仮型パラメータ、ジェネリック型は具象化不可能クラス
• elementsはObject型で生成する
• どこでキャストするかが問題
Public Stack() {elements = new E[DEFAULT_INITIAL_CAPACITY];
}
適切な無検査警告の抑制
• パターン1
– elementsはprivateフィールドであり、elements追加される値の型はpushメソッドからのEのみ
Public Stack() {elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
Objectの配列をキャストする
適切な無検査警告の抑制
• パターン2
private Object[] elements;
Public Stack() {elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public E pop() {if (size == 0) {
throw new EmptyStackException();}E result = (E) elements[--size];elements[size] = null;return result;
}要素を取り出すタイミングでキャ
スト
どちらの方法を採るべきか
• キャストの回数
– 要素を追加する毎にキャストよりも、配列ごとキャストする方が回数は少ない
• キャストの危険性
– 配列型への無検査キャストを抑制する方が危険
型パラメータに関する制約
• 基本的にはない
• プリミティブ型は、ボクシングされた基本データ型を使う
境界型パラメータ
• 型パラメータリストで、仮型パラメータを束縛する– Bounded Type Parameters
– 実型パラメータがDelayedのサブタイプであることを強制する
– extendsによって、仮型パラメータの範囲(境界)を定義
– すべての方は、それ自身のサブタイプ
Class DelayQueue<E extends Delayed> implements BlockingQueue<E>;
• 項目27
– ジェネリックメソッドを使用する
論点
1. row型警告
2. ジェネリックシングルトンファクトリー
3. 再帰型境界
単純な例
• セットの要素がすべて同じ型であることを強制する
– 境界ワイルドカード型(Bounded Wildcard Type)を使うことで、柔軟性を持たせることもできる(→項目28)
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {Set<E> result = new HashSet<>(s1);result.addAll(s2);return result;
}
このEはどのように決まるのか?
ジェネリックシングルトンファクトリー
• 論理的に型安全であることが確認できたなら、無検査警告を抑制してもよい
– Ex. 恒等式
• 安全である論理的根拠は?
再帰型境界
• 型パラメータをその型パラメータ自身が関係する何らかの式で制限する
– Ex. リスト中の最大値を返す関数の定義するた
めに、「相互比較可能」であることを表現したいとき
• extends Comparable?
• http://d.hatena.ne.jp/Nagise/20101101/1288629634
class static <T extends Comparable<T>> T max(List<T> list)
• 項目28
– APIの柔軟性向上のために境界ワイルドカードを使用する
論点
1. 境界ワイルドカードの必要性
2. 上限境界と下限境界の使い分け
3. 戻り値型にワイルドカードを使わない
4. ワイルドカード型をいつ使うか
5. ワイルドカード型を特定の型として捉えたい
?
?
?
論点1境界ワイルドカードの必要性
境界ワイルドカード
• ワイルドカードに制約を追加することで、受け取れる型の範囲を柔軟に定義する
– ワイルドカードを使わない場合、Iterable<Number>は、Number型しか受け取らない(不変)
void pushAll(Iterable<? extends E> src) {for (E e : src) {
push(e);}
}
境界ワイルドカードが必要ない場合
– 『T 型の変数の観点で規定されており、T に対
するジェネリック型の観点で規定されているわけではない』
public interface Box<T> {public T get();public void put(T element);
}
境界ワイルドカードが必要な場合
– Box<Number> に対する put(Box<Integer>) メソッドが見つからない
– Integer は Number であっても Box<Integer> は Box<Number> ではない
public interface Box<T> {public T get();public void put(T element);public void put(Box<T> box);
}
Box<Number> nBox = new BoxImpl<Number>();Box<Integer> iBox = new BoxImpl<Integer>();
nBox.put(iBox);
どうすればジェネリックになるか
• ワイルドカードに上限境界を設ける
– 共変と不変(反変)との妥協案
public interface Box<T> {public T get();public void put(T element);public void put(Box<? extends T> box);
}
論点2上限境界と下限境界の使い分け
上限境界か下限境界か
• PECS
– パラメータ化された型がTプロデューサーを表していれば、<? extends T>
– パラメータ化された型がTコンシューマーを表していれば、<? super T>
論点3
戻り値としてのワイルドカード型
戻り値型にワイルドカードは使わない
• ユーザーにワイルドカードを使わせるのはAPIとして設計を誤っている
public static <E> Set<? Extends E>union(Set<? extends E> s1, Set<? extends E> s2) {
:}
public static void main(String[] args) {:Set<String> aflCio = union(guys, stooges);Set<? extends String> aflCio = union(guys, stooges);
}
論点4ワイルドカード型をいつ使うか
型パラメータとワイルドカード
• どちらで定義するべきか
– 「単純で好ましい」(?)
– 「どのようなリストでも渡せる」(?)
public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);
論点5
ワイルドカード型を特定の型として捉えたい
互換性を検証できない
• 「put() の実際のパラメーターの型が正式
なパラメーターの型と互換性があることを検証できないため、put() を呼び出すことはできない」
public interface Box<T> {public T get();public void put(T element);
}
public void rebox(Box<?> box) {box.put(box.get());
}
ワイルドカードキャプチャー
• V によって任意の未知の型も表すことができる– 「名前を復活させる」
public interface Box<T> {public T get();public void put(T element);
}
public void rebox(Box<?> box) {reboxHelper(box);
}
private <V> void reboxHelper(Box<V> box) {box.put(box.get());
}
参考文献
• http://www.ibm.com/developerworks/jp/java/library/j-jtp04298.html
• http://www.ibm.com/developerworks/jp/java/library/j-jtp07018.html
• 項目29
– 型安全な異種コンテナーを検討する
論点
1. Classクラスがジェネリクス型であることを利用して、異種コンテナーを実装する
– ネストされた非境界型パラメーター
– Class#typeによる動的キャスト
問題点1
• Favoritesインスタンスの型安全性が完全ではない
問題点2
• 具象化不可能クラスを格納できない
– Classオブジェクトを取得できない