Your SlideShare is downloading. ×
0
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
ジェネリクスの基礎とクラス設計への応用
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

ジェネリクスの基礎と クラス設計への応用

15,974

Published on

JJUG CCC 2013 Fall #ccc_h1

JJUG CCC 2013 Fall #ccc_h1

Published in: Technology
0 Comments
43 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
15,974
On Slideshare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
80
Comments
0
Likes
43
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. ジェネリクスの基礎と クラス設計への応用 2013 年版 Twetter : @nagise はてな : Nagise 所属    java-ja   北陸エンジニアグループ
  • 2. 導入 Java 1.4 までのコード ArrayList list = new ArrayList(); list.add("hoge"); String s = (String) list.get(0);
  • 3. ジェネリクスのない世界  Object 型からのダウンキャストが必要  キャスト失敗は実行時例外 ClassCastException  動かしてみないと間違いに気づかない  ときに動かしても気づかない  ドキュメントなどで型を明示
  • 4. 導入 Java 1.5 からのコード ArrayList<String> list = new ArrayList<String>(); list.add("hoge"); String s = list.get(0);
  • 5. ジェネリクス以後の世界  コレクション API でダウンキャストが不 要に  コンパイル時点で型の間違いに気づく  IDE によってはコンパイル前に気づく  型システムが型を把握してくれる  必修事項が増えました
  • 6. 今日のキーワード Generics Hell じぇねりくす へる 日本風に言えば「まったく Java のジェネリクスは地獄だぜ!」 略して「ヘル - い」などと表現したりする
  • 7. 入門編  ジェネリックな API を利用するのに必要 な知識を身に着ける  2種類のスコープ  3種の山括弧  代入について
  • 8. ジェネリクスの2種類のスコー プ メソッドの中でのみ有効なジェネリクス public static <T> void hoge(T t) { … } インスタンスの中でのみ有効なジェネリク ス class Hoge<T> { Tt; ... }
  • 9. ジェネリックメソッド メソッドの引数と戻 り値の型の関係を表 す  メソッドの複数の引  数の型の関係を表す
  • 10. ジェネリックメソッドの例 java.util.Collections クラス リスト内に出現する指定された値を すべてほかの値に置き換えます。 public static <T> boolean replaceAll (List<T> list, T oldVal, T newVal) 3つの引数が List<T> 型、 T 型、 T 型という関係性
  • 11. ジェネリックメソッドの例 java.util.Collections クラス 指定されたオブジェクトだけを格納している 不変のセットを返します public static <T> Set<T> singleton(T o) 引数が T 型で、戻り値が Set<T> 型という関係性
  • 12. ジェネリックメソッドの例 java.util.Collections クラス デフォルトの乱数発生の元を使用して、 指定されたリストの順序を無作為に入れ替えます。 public static void shuffle(List<?> list) 引数1つだけなので関連性を示す必要がない。 無駄に型変数を定義せず、ワイルドカードを利用して List<?> 型として宣言するほうが使い勝手が良い
  • 13. ジェネリックメソッドの呼び出 し方 List<String> list = new ArrayList<String>(); list.add("hoge"); Collections.<String>replaceAll(list, "hoge", "piyo"); List<Integer> intList = new ArrayList<Integer>(); intList.add(42); Collections.<Integer>replaceAll(intList, 42, 41); static の場合はクラス名 .< バインド型 > メソッド名 (…) インスタンスメソッドの場合は変数名 .< バインド型 > メソッド名 (…) this に対するメソッド呼び出し時、明示的にバインドしたい場合は this を省略できない
  • 14. インスタンスの I/O 複数のメソッ ド間の引数・戻 り値の型の関係 性  公開されてい  るフィールド 内部クラス 
  • 15. ジェネリックなインスタンスの 例 java.util.ArrayList の例 public boolean add(E e) public E get(int index) 複数のメソッド間で型の関連性を表現している
  • 16. ジェネリクスと構造化 ジェネリクスメソッド、ジェネリッククラスとも に ある囲いを作って、その内外のやりとりを する場合のデータ型を型変数で抽象化する。 そのため、ジェネリックな設計をするためには 前提としてきれいな構造化をする必要がある。
  • 17. 文法のはなし public class Syntax<T> implements Iterable<String> { public <X> void hoge(X x) { List<X> list = new ArrayList<X>(); list.add(x); } @Override public Iterator<String> iterator() { return null; } } 似て非なる<>を色分けしてみました
  • 18. 3種類の<> 型変数の宣言 class Hoge<T> {} public <T> void hoge() {}  型変数のバインド  new ArrayList<String>(); class Hoge extends ArrayList<String> {} Collections.<String>replaceAll( list, "hoge", "piyo"); パラメータ化された型 ( parameterized type ) List<String> list; 
  • 19. 型のバインド ジェネリックメソッドの例 宣言側 仮型引数( type-parameter ) public static <T> boolean replaceAll (List<T> list, T oldVal, T newVal) 利用側 実型引数( type-argument ) Collections.<String>replaceAll( list, "hoge", "piyo");
  • 20. 型のバインド ジェネリッククラスの例 宣言側 仮型引数( type-parameter ) public class ArrayList<E> {...} 利用側 実型引数( type-argument ) List<String> list = new ArrayList<String>();
  • 21. ( 参考 ) 仮引数と実引数 メソッドの仮引数と実引数との対比 宣言側 仮引数( parameter ) public void hoge(int index){...} 利用側 実引数( argument ) hoge(123);
  • 22. 型推論 ジェネリックメソッド 正書法 Collections.<String>replaceAll( list, "hoge", "piyo"); 型推論 Collections.replaceAll( list, "hoge", "piyo");
  • 23. ダイヤモンド演算子 ジェネリッククラス 正書法 List<String> list = new ArrayList<String>(); ダイヤモンド演算子 List<String> list = new ArrayList<>();
  • 24. 推論器 ジェネリックメソッドの型推論と ダイヤモンド演算子は同じ機構を利用し ている Java7 では推論が弱いが、 Java8 ではラ ムダのために 型推論が強化された
  • 25. 継承によるバインド public class StringList extends ArrayList<String> {} というクラスを作ると StringList list = new StringList(); list.add("hoge"); String str = list.get(0); といったように、型変数のないクラスになる
  • 26. 複雑になる前に Map<String, Map<Integer, List<Hoge>>> みたいにジェネリクスが複雑化するようなら 適度なレベルでした継承クラスを作ることで シンプルでわかりやすくなる クラスを作るのをサボらない
  • 27. サンプルのクラス
  • 28. パラメータ化された型の代入互換 性 B[] arrayB = new B[1]; A[] arrayA = arrayB; arrayA[0] = new B2(); → ArrayStoreException が発生 List<B> listB = new ArrayList<B>(); List<A> listA = listB; listA.add(new B2()); → コンパイルエラー
  • 29. 異なる代入互換性 B の集合型 ArrayList<B> は A の集合型 ArrayList<A> の 代理をできない
  • 30. ワイルドカードの使用 ArrayList<? extends B> の範囲 ArrayList<? super B> の範囲
  • 31. <? extends ~ > の入力制約 List<? extends B> には add() できない  List<C> 型を代入  B 型を add() とする  List<C> に B 型が add() される ←矛盾  get() は B 型の戻り値を返す
  • 32. <? super ~ > の出力制約 ArrayList<? super B> には B 型を add() できる  ただし get() は Object 型としてしか返せな い 
  • 33. まとめ 型変数でメソッドやインスタンスの I/O の 型の関連性を表す → まずは綺麗なオブジェクト指向の設計を 文法をマスターするには 3種類の<>を意識する 代入互換性は訓練あるのみ
  • 34. 中級編  ジェネリックな API を設計するのに必要 な知識を身に着ける  型変数の宣言  再帰的ジェネリクス  内部クラス  リフレクション
  • 35. 型変数の境界 class Hoge<T extends B> 型変数の宣言時には境界を指定できる new Hoge<A>(); ← new Hoge<B>(); ← new Hoge<B2>(); ← new Hoge<C>(); ← NG OK NG OK
  • 36. 型変数の境界 class Hoge <T extends A & Serializable> & で繋いでインタフェースを 境界に加えることができる
  • 37. 再帰ジェネリクス public abstract class Hoge<T extends Hoge<T>> { public abstract T getConcreteObject(); } Hoge 型の型変数 T は Hoge 型を継承していなくてはなら ない
  • 38. 再帰する型変数へのバインド new でバインドできない Hoge<?> hoge = new Hoge<Hoge<...>>(); 再帰ジェネリクスは継承で扱う public class Piyo extends Hoge<Piyo> {...}
  • 39. 再帰ジェネリクスの効能 public class Piyo extends Hoge<Piyo> { @Override public Piyo getConcreteObject() { return this; } } このようにすると Piyo piyo = new Piyo(); Piyo piyo2 = piyo.getConcreteObject();
  • 40. 再帰ジェネリクスの効能 public abstract class Hoge<T extends Hoge<T>> { public abstract T getConcreteObject(); } 親クラスで定義されたメソッドなのに 子クラスの具象型を扱うことができる…!
  • 41. 再帰ジェネリクスの使用例 java.lang.Enum クラスの例 public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { public final int compareTo(E o) {…} }
  • 42. 再帰ジェネリクスの使用例 enum SampleEnum { ONE, TWO, } に対して SampleEnum.ONE.compareTo(SampleEnum.TWO); は安全に compareTo できる。 他の enum と比較しようとするとコンパイルエラー
  • 43. 内部クラスのジェネリクス 内部クラスは外部クラスの型変数を利用できる public class Outer<T> { public class Inner { T genericField; } }
  • 44. 内部クラスの new の仕方 内部クラスの new の仕方知ってますか? Outer<String> outer = new Outer<String>(); Outer<String>.Inner inner = outer.new Inner(); 内部クラスは外部クラスのインスタンスに紐づく。 ジェネリクスは「インスタンスの I/O で型の関連性を示 す」 の I/O には内部クラスも含まれます
  • 45. 内部クラスの利用例 public class SampleList<T> extends ArrayList<T> { @Override public Iterator<T> iterator() { return super.iterator(); } public class SampleIterator implements Iterator<T> { // 略 } } そもそも内部クラスの使いどころが難しいですが。
  • 46. リフレクション • 型変数に格納されるインスタンスの型情 報は欠落する(イレイジャ方式) • メソッド引数やフィールド宣言に用いら れるパラメタライズド・タイプの型情報 は欠落しない http://d.hatena.ne.jp/Nagise/20121226/1356531878 Java のジェネリクスとリフレクション
  • 47. リフレクション java.lang.reflect.Type を利用 実装クラス / サブ interface •Class – いつもの •GenericArrayType – ジェネリックな配列。きも い •ParameterizedType – パラメタライズドタイプ •TypeVariable<D> - 型変数 •WildcardType – ワイルドカード ダウンキャストして使う
  • 48. リフレクション java.lang.reflect.Type を返すメソッドを利用する •Class # getGenericSuperclass() : Type •Class # getGenericInterfaces() : Type[] •Method # getGenericParameterTypes() : Type[] •Method # getGenericReturnType() : Type •Field # getGenericType() : Type などなど
  • 49. まとめ ジェネリクスが複雑になりすぎないように 継承でのバインドの利用を検討する 再帰ジェネリクスを応用すれば サブクラスで具象型を扱える 内部クラスもスコープ範囲内 リフレクションでフィールドやメソッド引 数・戻り値のパラメタライズドタイプの型 をとれる
  • 50. 上級編  ジェネリックな API の設計例  利用側のコードとともによい設計を考え る
  • 51. new T() したい 動機   DB のマッパーや Web システムの Controller のようなフレームワークを作っ た場合、 取り扱うオブジェクトの型をジェネリクス で定義し、インスタンスを生成してデータ をマッピングして渡したい
  • 52. コンストラクタ Java のオブジェクト指向の一般論おさらい interface や親クラスとして 振る舞うことが要求される 具象型の特有の情報を 押し込める場所はコンストラクタ
  • 53. コンストラクタ コンストラクタの引数の形は継承で 制約できません やりたければ Factory クラス作れ 一般にインスタンス生成は抽象化しにくい
  • 54. Factory の実装例 interface HogeFactory<T extends A> { /** デフォルトコンストラクタ的な */ T newInstance(); } インスタンスの生成に必要なデータを Factory で制約 public class HogeTemplate { public <T> T template(HogeFactory<T> factory) { return factory.newInstance(); } } こうすればインスタンスの生成は可能になる、が面倒
  • 55. 妥協例 public <T extends A> T template(T obj) { try { return (T)obj.getClass().newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public <T extends A> T template(Class<T> clazz) { try { return (T)clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } デフォルトコンストラクタがあることが前提
  • 56. C# の例 class MyGenericClass<T> where T : new() {} T 型にデフォルトコンストラクタがあることという制約。 デフォルトコンストラクタがあることを保証させることで インスタンス生成を可能とする。 この制約はダサい制約ではあるが妥当な妥協点か。 Java でもリフレクションで生成する場合に デフォルトコンストラクタがあることという制限をつけがち。
  • 57. 継承によるバインドの場合 Class # getGenericSuperclass() という福音 継承によるバインドであれば、型情報から型変数に何がバ インドされたかを知ることができる Type を ParameterizedType にキャスト getActualTypeArguments() でバインドされた実体 実体の Type を Class にキャストして newInstance() ただし、デフォルトコンストラクタがあるものとする http://d.hatena.ne.jp/Nagise/20130815
  • 58. ジェネリックなデザインパター ン Template Method パターン 虫食いのテンプレートを作る。 左図の処理 ABC は abstract メソッド。 サブクラスではオーバーライドして実装を書く。 ここに先の継承によるバインドと バインドされた型のリフレクションでの newInstance() を複合すると便利 処理 A 処理 B 処理 C
  • 59. まとめ New T() したくなるシチュエーションには 継承によるバインド+リフレクション を使えないか検討する Template nethod パターンと複合させると フレームワークに応用できるかも
  • 60. 変態編  何かに使えるかもしれない
  • 61. 再帰での相互参照 二つの型の具象型が、相互に相手の具象型を知っている class Hoge<H extends Hoge<H, P>, P extends Piyo<H, P>> class Piyo<H extends Hoge<H, P>, P extends Piyo<H, P>> 実装は class HogeImpl extends Hoge<HogeImpl, PiyoImpl> class PiyoImpl extends Piyo<HogeImpl, PiyoImpl>
  • 62. 相互再帰+1 汎用型変数 T を追加してみる class Hoge<T, H extends Hoge<T, H, P>, P extends Piyo<T, H, P>> class Piyo<T, H extends Hoge<T, H, P>, P extends Piyo<T, H, P>> 実装クラス class HogeImpl<T> extends Hoge<T, HogeImpl<T>, PiyoImpl<T>> class PiyoImpl<T> extends Piyo<T, HogeImpl<T>, PiyoImpl<T>> やりすぎです
  • 63. 内部クラスでグルーピング 二つのクラスを囲うクラスを作って Hoge と Piyo を内部クラスにすれば…! public abstract class Outer <H extends Outer<H, P>.Hoge, P extends Outer<H, P>.Piyo> { public abstract class Hoge { public abstract P getConcretePiyo(); } public abstract class Piyo { public abstract H getConcreteHoge(); } } やりすぎです
  • 64. 型変数の部分適用 複数の型変数がある型を2段階に分けてバインド public class Outer<K> { public class Inner<V> extends HashMap<K, V> {} } Outer<String> o = new Outer<String> (); HashMap<String, Integer> inner = o. new Inner<Integer>();
  • 65. 応用例 型変数の部分適用 http://d.hatena.ne.jp/Nagise/20110124/1295874192 Java による高階型変数の実装 public class Hoge extends KeyGroup<Hoge> { private static final Hoge singleton = new Hoge(); public static final Hoge.Key<String> HOGE_STRING = singleton.new Key<String>(); } public class KeyValue<KG extends KeyGroup<KG>> { public <T> void put(KeyGroup<KG>.Key<T> key, T value){} } KeyValue<Hoge> tm = new KeyValue<Hoge>(); tm.put(Hoge.HOGE_STRING, "hoge");
  • 66. より深く知りたい場合の資料 当セッションは主に blog に記述した事項を再編集 してまとめたものである。 Blog プログラマーの脳みそ カテゴリー Generics を参照 http://d.hatena.ne.jp/Nagise/searchdiary?word= %2A%5BGenerics%5D Generics Hell に遭遇したら Twitter で @nagise にご一報ください。

×