前回に続き、Effective Javaネタ。
確かにメソッドの引数に関してはその通りだと思う。 しかし、メソッド使用者の利便性を考えると、"戻り値型として境界ワイルドカード型を使わないでください。"とある。こちらについてはどうだろうか。
funcはリストを作成するメソッドなので、戻り値をより一般的にしようとして、List<? super T> func<T>(T a)とすると困ったことが起こる。
ワイルドカード型のジェネリック変数にワイルドカードでないジェネリック型を代入することはできるけど、ワイルドカードでないジェネリック型の変数にワイルドカード型を代入することはできない。
というか、代入先のワイルドカードがより広い範囲を指しており、範囲に矛盾がなければ代入が可能ということになる。
だから、イメージとしてはワイルドカード型は非ワイルドカード型を汎化したもので、非ワイルドカード型はワイルドカード型を特化したものと考えるといいのかもしれない。
ワイルドカード型同士も、より狭い範囲を指している型ほど特化している。より広い範囲を指している型ほど汎化しているということになる。
PECS
Effective Java 項目31 APIの柔軟性向上のために境界ワイルドカードを使う によると"最大限の柔軟性のためには、プロデューサ(生産者)かコンシューマ(消費者)を表す入力パラメータに対してワイルドカード型を使ってください。"とある。確かにメソッドの引数に関してはその通りだと思う。 しかし、メソッド使用者の利便性を考えると、"戻り値型として境界ワイルドカード型を使わないでください。"とある。こちらについてはどうだろうか。
戻り値としてリストが返ってくるメソッド
例えば、リストを生成するメソッド(まさにProducer!)List<T> func<T>(T a)を考えよう。List<Integer> li = func(1);
funcはリストを作成するメソッドなので、戻り値をより一般的にしようとして、List<? super T> func<T>(T a)とすると困ったことが起こる。
// コンパイルエラー! List<Integer> li = func(1);
ジェネリック型同士の汎化・特化関係
...と、ここで、これはPECS原則と代入の方向を勘違いしていることが原因だと気づいた。そもそも、戻り値をどう使うかはメソッド使用者側の問題なので、メソッドのデザインとして、PECSの原則には当てはまらない。ワイルドカード型のジェネリック変数にワイルドカードでないジェネリック型を代入することはできるけど、ワイルドカードでないジェネリック型の変数にワイルドカード型を代入することはできない。
というか、代入先のワイルドカードがより広い範囲を指しており、範囲に矛盾がなければ代入が可能ということになる。
List<Integer> li = new ...; // ? extends Number は Integer よりも広い. List<? extends Number> len = li; // ? は ? extends Number よりも広い. List<?> lq = len; List<Number> ln = new ...; // ? super Integer は Number よりも広い. List<? super Integer> lsi = ln;
だから、イメージとしてはワイルドカード型は非ワイルドカード型を汎化したもので、非ワイルドカード型はワイルドカード型を特化したものと考えるといいのかもしれない。
ワイルドカード型同士も、より狭い範囲を指している型ほど特化している。より広い範囲を指している型ほど汎化しているということになる。
List<? extends Number> len = ...; // ? extends Object は ? extends Number よりも広い. List<? extends Object> leo = len;
0 件のコメント:
コメントを投稿