36
Effective Java 輪読会 2013/01/08 開発部 田中

Effective java 輪読会 第5章 項目26-29

Embed Size (px)

Citation preview

Page 1: Effective java 輪読会 第5章 項目26-29

Effective Java 輪読会

2013/01/08

開発部田中

Page 2: Effective java 輪読会 第5章 項目26-29

第5章 ジェネリックス(項目26~29)

Page 3: Effective java 輪読会 第5章 項目26-29

• 項目26

– ジェネリック型を使用する

Page 4: Effective java 輪読会 第5章 項目26-29

ジェネリック化

• Stackクラス– 互換性のために要素の型をObjectにしている

• 要素を取り出す際、クライアントでキャストが必要

• Object型を仮型パラメータEで置き換えることで、クラスをジェネリック化(一般化)する

• ジェネリック化により、コンパイル時に型の検査を行う

Page 5: Effective java 輪読会 第5章 項目26-29

ジェネリック配列生成の禁止

• Stackクラスでコンパイルエラーが発生

– 仮型パラメータ、ジェネリック型は具象化不可能クラス

• elementsはObject型で生成する

• どこでキャストするかが問題

Public Stack() {elements = new E[DEFAULT_INITIAL_CAPACITY];

}

Page 6: Effective java 輪読会 第5章 項目26-29

適切な無検査警告の抑制

• パターン1

– elementsはprivateフィールドであり、elements追加される値の型はpushメソッドからのEのみ

Public Stack() {elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];

}

Objectの配列をキャストする

Page 7: Effective java 輪読会 第5章 項目26-29

適切な無検査警告の抑制

• パターン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;

}要素を取り出すタイミングでキャ

スト

Page 8: Effective java 輪読会 第5章 項目26-29

どちらの方法を採るべきか

• キャストの回数

– 要素を追加する毎にキャストよりも、配列ごとキャストする方が回数は少ない

• キャストの危険性

– 配列型への無検査キャストを抑制する方が危険

Page 9: Effective java 輪読会 第5章 項目26-29

型パラメータに関する制約

• 基本的にはない

• プリミティブ型は、ボクシングされた基本データ型を使う

Page 10: Effective java 輪読会 第5章 項目26-29

境界型パラメータ

• 型パラメータリストで、仮型パラメータを束縛する– Bounded Type Parameters

– 実型パラメータがDelayedのサブタイプであることを強制する

– extendsによって、仮型パラメータの範囲(境界)を定義

– すべての方は、それ自身のサブタイプ

Class DelayQueue<E extends Delayed> implements BlockingQueue<E>;

Page 11: Effective java 輪読会 第5章 項目26-29

• 項目27

– ジェネリックメソッドを使用する

Page 12: Effective java 輪読会 第5章 項目26-29

論点

1. row型警告

2. ジェネリックシングルトンファクトリー

3. 再帰型境界

Page 13: Effective java 輪読会 第5章 項目26-29

単純な例

• セットの要素がすべて同じ型であることを強制する

– 境界ワイルドカード型(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はどのように決まるのか?

Page 14: Effective java 輪読会 第5章 項目26-29

ジェネリックシングルトンファクトリー

• 論理的に型安全であることが確認できたなら、無検査警告を抑制してもよい

– Ex. 恒等式

• 安全である論理的根拠は?

Page 15: Effective java 輪読会 第5章 項目26-29

再帰型境界

• 型パラメータをその型パラメータ自身が関係する何らかの式で制限する

– Ex. リスト中の最大値を返す関数の定義するた

めに、「相互比較可能」であることを表現したいとき

• extends Comparable?

• http://d.hatena.ne.jp/Nagise/20101101/1288629634

class static <T extends Comparable<T>> T max(List<T> list)

Page 16: Effective java 輪読会 第5章 項目26-29

• 項目28

– APIの柔軟性向上のために境界ワイルドカードを使用する

Page 17: Effective java 輪読会 第5章 項目26-29

論点

1. 境界ワイルドカードの必要性

2. 上限境界と下限境界の使い分け

3. 戻り値型にワイルドカードを使わない

4. ワイルドカード型をいつ使うか

5. ワイルドカード型を特定の型として捉えたい

Page 18: Effective java 輪読会 第5章 項目26-29

論点1境界ワイルドカードの必要性

Page 19: Effective java 輪読会 第5章 項目26-29

境界ワイルドカード

• ワイルドカードに制約を追加することで、受け取れる型の範囲を柔軟に定義する

– ワイルドカードを使わない場合、Iterable<Number>は、Number型しか受け取らない(不変)

void pushAll(Iterable<? extends E> src) {for (E e : src) {

push(e);}

}

Page 20: Effective java 輪読会 第5章 項目26-29

境界ワイルドカードが必要ない場合

– 『T 型の変数の観点で規定されており、T に対

するジェネリック型の観点で規定されているわけではない』

public interface Box<T> {public T get();public void put(T element);

}

Page 21: Effective java 輪読会 第5章 項目26-29

境界ワイルドカードが必要な場合

– 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);

Page 22: Effective java 輪読会 第5章 項目26-29

どうすればジェネリックになるか

• ワイルドカードに上限境界を設ける

– 共変と不変(反変)との妥協案

public interface Box<T> {public T get();public void put(T element);public void put(Box<? extends T> box);

}

Page 23: Effective java 輪読会 第5章 項目26-29

論点2上限境界と下限境界の使い分け

Page 24: Effective java 輪読会 第5章 項目26-29

上限境界か下限境界か

• PECS

– パラメータ化された型がTプロデューサーを表していれば、<? extends T>

– パラメータ化された型がTコンシューマーを表していれば、<? super T>

Page 25: Effective java 輪読会 第5章 項目26-29

論点3

戻り値としてのワイルドカード型

Page 26: Effective java 輪読会 第5章 項目26-29

戻り値型にワイルドカードは使わない

• ユーザーにワイルドカードを使わせるのは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);

}

Page 27: Effective java 輪読会 第5章 項目26-29

論点4ワイルドカード型をいつ使うか

Page 28: Effective java 輪読会 第5章 項目26-29

型パラメータとワイルドカード

• どちらで定義するべきか

– 「単純で好ましい」(?)

– 「どのようなリストでも渡せる」(?)

public static <E> void swap(List<E> list, int i, int j);

public static void swap(List<?> list, int i, int j);

Page 29: Effective java 輪読会 第5章 項目26-29

論点5

ワイルドカード型を特定の型として捉えたい

Page 30: Effective java 輪読会 第5章 項目26-29

互換性を検証できない

• 「put() の実際のパラメーターの型が正式

なパラメーターの型と互換性があることを検証できないため、put() を呼び出すことはできない」

public interface Box<T> {public T get();public void put(T element);

}

public void rebox(Box<?> box) {box.put(box.get());

}

Page 31: Effective java 輪読会 第5章 項目26-29

ワイルドカードキャプチャー

• 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());

}

Page 33: Effective java 輪読会 第5章 項目26-29

• 項目29

– 型安全な異種コンテナーを検討する

Page 34: Effective java 輪読会 第5章 項目26-29

論点

1. Classクラスがジェネリクス型であることを利用して、異種コンテナーを実装する

– ネストされた非境界型パラメーター

– Class#typeによる動的キャスト

Page 35: Effective java 輪読会 第5章 項目26-29

問題点1

• Favoritesインスタンスの型安全性が完全ではない

Page 36: Effective java 輪読会 第5章 項目26-29

問題点2

• 具象化不可能クラスを格納できない

– Classオブジェクトを取得できない