Effective java 輪読会 第5章 項目23-25
-
Upload
appresso-engineering-team -
Category
Technology
-
view
351 -
download
1
Transcript of Effective java 輪読会 第5章 項目23-25
Effective Java 輪読会 第 4回(項目 23 ~ 25 )2013/1/8
開発部 野口
項目 23 新たなコードで原型を使用しない
ジェネリック型 1 つ以上の型パラメータを宣言に持つクラス
やインタフェース ジェネリッククラス/ジェネリックインタフェー
ス 例) List<E> はジェネリックインタフェー
ス E は型パラメータ 「 E のリスト」と読む 単に List とも読む
パラメータ化された型 クラス名やインタフェース名の後に、 <> で
囲んだ実型パラメータのリストが続くもの 例) List<String>
「文字列のリスト」と読む
原型 実型パラメータを伴わないで使用されるジェ
ネリック型の名前 例) List<E> に対応する原型は List 原型 List は、ジェネリックスがプラット
フォームに追加される前のインタフェース型 List と同様に振る舞う
リリース 1.5 より前のコレクション宣言
// Stamps インスタンスだけを含んでいるprivate final Collection stamps = … ;
stamps.add(new Stamp()); // これが想定している挿入stamps.add(new Coin()); // これもコンパイルエラーにならない
for (Iterator I = stamps.iterator(); i.hasNext();) { Stamp s = (Stamp) i.next(); // ClassCastException がスローされる}
パラメータ化されたコレクション型( 1/2 )
private final Collection<Stamp> stamps = … ;
stamps.add(new Stamp()); // これは想定している挿入stamps.add(new Coin()); // これはコンパイルエラーになる
パラメータ化されたコレクション型( 2/2 )
// キャストも不要にfor (Stamp s : stamps) {
... // 切手で何かをする}
for (Iterator<Stamp> i = stamps.iterator(); i.hasNext();) {
Stamp s = i.next(); // キャストなし... // 切手で何かをする
}
間違えるのはどんなとき? java.sql.Date インスタンスのコレクション
に、 java.util.Date インスタンスを入れてしまうとか……。
原型の使用 原型を使用すると、ジェネリックスの安全性
と表現力のすべてを失うことになる 原型は、移行互換性のために残されている
List と List<Object> の違い List は、ジェネリック型検査が行われない
パラメータ List に List<String> を渡すことができる List<String> は List のサブタイプ
List<Object> は、どのような型のオブジェクトでも保持できることをコンパイラに明示的に伝えている List<Object> に List<String> を渡すことはできな
い List<String> は、 List<Object> のサブタイプでは
ない(!) 具体例は、 pp.110 を参照
非境界ワイルドカード型 ジェネリック型を使用したいけれど、実際の
型パラメータが何であるか分からなかったり、気にしたりしない場合にクエスチョン記号を使用できる 例)ジェネリック型 Set<E> に対するワイルド
カード型は Set<?> 「何らかの型のセット」と読む どのようなセットも保持できる(!)
Set<?> と Set の違い Set<?> は安全 Set は安全ではない Set<?> ( Collection<?> ) には、( null
以外の)いかなる要素も入れることができない
Set ( Collection )には、どのような要素も入れることができる
例外 (1/2)
クラスリテラルでは、原型を使用しなければならない 例) List<String>.class ではなく、 List.class
を使用する必要がある ジェネリック型情報が実行時に消されているから
例外 (2/2)
非境界ワイルドカード型以外のパラメータ化された型に対する instanceof 演算子の使用は許されていない やはり、ジェネリック型情報が実行時に消されて
いるから 非境界ワイルドカード型について
も、 instanceof の使用はできるが、原型に対して使用した場合と違いがない
原型を使用するのがよい
まとめ (1/2)
新たなコードでは原型は使用しない 実行時例外となる可能性があるため
原型はジェネリックス導入前のコードとの互換性と相互運用のためだけに提供されている
まとめ (2/2)
Set<Object> は任意の型のオブジェクトを含むことが可能なセットを表しているパラメータ化された型<安全>
Set<?> は何らかの不明な型のオブジェクトだけを含むことが可能なセットを表しているワイルドカード型<安全>
Set はジェネリック型システムから外れている原型<安全でない>
項目 24 無検査警告を取り除く
無検査警告
Set<Lark> exaltation = new HashSet();
無検査警告を取り除く
取り除くことが可能なすべての無検査警告を取り除くこと すべての警告を取り除けば、コードが型安全であると
安心できる 実行時に ClassCastException が発生しない
Set<Lark> exaltation = new HashSet<Lark>();
無検査警告を抑制する 以下の場合にの
み、 @SuppressWarnings("unchecked") アノテーションで警告を抑制する 警告を取り除くことができない 警告を起こしているコードが型安全
SuppressWarnings アノテーション
個々のローカル変数宣言からクラス全体まで、どのような粒度でも使用できる
できる限り最小のスコープに対して使用すること 変数宣言、非常に短いメソッドやコンストラクタ
等 クラス全体には決して使用しない
重大な警告を隠蔽してしまうため
ローカル変数宣言へアノテーションを移動させる
例) pp.114 toArray メソッド return 文は宣言ではないの
で、 SuppressWarnings アノテーションを付けることはできないが、ローカル変数を宣言することでスコープを最小限にすることができる
@SuppressWarnings("unchecked") アノテーション @SuppressWarnings("unchecked") アノ
テーションを使用する時には、そうするのが安全である理由を述べるコメントを必ず追加する 他の人がコードを理解することを助ける 計算が安全ではなくなるような変更を誰かが行う
可能性を減らす コメントについて考えるうちに、やはり安全では
ないということに気づく可能性もある
まとめ
無検査警告は重要、無視してはいけない 実行時の ClassCastException の可能性を表し
ている 取り除くことが不可能で、それが実際には型
安全であると明確に示せる場合のみ、最小限のスコープで警告を抑制する コメントにその理由を残す
項目 25 配列よりリストを選ぶ
配列とジェネリック型の違い 1 (of 2) 配列は共変( covariant )
Sub が Super のサブタイプならば、配列型 Sub[] は Super[] のサブタイプ
ジェネリックスは不変( invariant ) Type1 と Type2 に対して、 List<Type1> は
List<Type2> のサブタイプでもなければスーパータイプでもない
共変な配列は実行時に失敗する
Object[] objectArray = new Long[1];objectArray[0] = “I don‘t fit in”; // ArrayStoreException <実行時エラー>
不変なジェネリック型はコンパイル時に失敗する
List<Object> ol = new ArrayList<Long>(); // 互換性のない型<コンパイルエラー>ol.add("I don't fit in");
配列とジェネリック型の違い 2 (of 2) 配列は具象化されている
実行時にその要素型を知っていて、強制する ジェネリックスはイレイジャで実装されてい
る コンパイル時にのみ型制約を強制し、実行時には
要素の型情報を廃棄(イレイズ)する
ジェネリック配列の生成が許されていない理由 型安全ではないから
例) pp.117 「なぜジェネリック配列生成が許されないのか」 もしこの例がコンパイルされてしまったら、行 (5)
で ClassCastException が発生する それを防ぐために、行 (1) でコンパイルエラーとな
る
具象化不可能型
その実行時の表現がコンパイル時の表現よりも情報が少ない型
具象化可能なパラメータ化された型は、非境界ワイルドカード型のみ
非境界ワイルドカード型の配列の生成は許されているが、まれにしか有用ではない
可変長引数について
可変長引数のメソッドでジェネリック型を使用した場合、警告が発生する
その場合、抑制するか、 API として可変長引数とジェネリック型の混在を避けるかする
非ジェネリックスからジェネリックスへの変更例 pp.118 上部 ジェネリックスを使用しない簡約、
そして並行性の欠陥↓ pp.118 下部 ジェネリックスを用いず、並行性の欠陥もない簡約↓
pp.119 上部 簡約の単純なジェネリック版 - コンパイルされない↓
pp.119 下部 リストに基づく簡約 ここで synchronized(list) としているのは、 new
によるインスタンス生成がアトミックでないから? →そのはず。
まとめ
配列は共変で具象化されている 実行時の型安全性を提供するが、コンパイル時の
型安全性は提供しない ジェネリックスは不変でイレイズされる
実行時の型安全性を提供しないが、コンパイル時の型安全性を提供する
配列とジェネリックスの混在によってコンパイル時エラーや警告が出る場合、配列をリストに置換することを考えるとよい