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

Post on 11-Jul-2015

358 Views

Category:

Technology

6 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

}

• 項目29

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

論点

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

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

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

問題点1

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

問題点2

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

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

top related