47
ジジジジジジジジジジジジ Twetter : @nagise ジジジ : Nagise ジジ ジジ () Abby java-ja ジジジジジジジジジジジ

ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

  • Upload
    nagise

  • View
    5.494

  • Download
    0

Embed Size (px)

DESCRIPTION

JJUG CCC 2012 Fall のセッション資料 #jjug_r12

Citation preview

Page 1: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリクスの基礎と応用

Twetter : @nagiseはてな : Nagise

所属 (株) Abby  java-ja 北陸エンジニアグループ

Page 2: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリクスのスコープメソッドの中でのみ有効なジェネリクス

public static <T> void hoge(T t) { … }

インスタンスの中でのみ有効なジェネリク

class Hoge<T> {

T t ;

...

}

Page 3: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックメソッド

メソッドの引数と戻り値の型の関係を表すメソッドの複数の引

数の型の関係を表す

Page 4: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックメソッドの例

java.util.Collections クラス

リスト内に出現する指定された値をすべてほかの値に置き換えます。

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)

3つの引数が List<T>型、 T 型、 T 型という関係性

Page 5: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックメソッドの例

java.util.Collections クラス

指定されたオブジェクトだけを格納している不変のセットを返します

public static <T> Set<T> singleton(T o)

引数が T 型で、戻り値が Set<T>型という関係性

Page 6: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックメソッドの例

java.util.Collections クラス

デフォルトの乱数発生の元を使用して、指定されたリストの順序を無作為に入れ替えます。

public static void shuffle(List<?> list)

引数1つだけなので関連性を示す必要がない

Page 7: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックメソッド

メソッドの引数と戻り値の型の関係を表すメソッドの複数の引

数の型の関係を表す

Page 8: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックメソッドの呼び出し方

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

Page 9: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

インスタンスの I/O

複数のメソッド間の引数・戻り値の型の関係性公開されてい

るフィールド内部クラス

Page 10: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ジェネリックなインスタンスの例

java.util.ArrayListの例

public boolean add(E e)public E get(int index)

複数のメソッド間で型の関連性を表現している

Page 11: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

文法のはなし

public class Syntax<T>implements Iterable<String> {

public <X> void hoge(X x) {List<X> list = new ArrayList<X>();list.add(x);

}@Overridepublic Iterator<String> iterator() {return null;

}}似て非なる<>を色分けしてみました

Page 12: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

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;

Page 13: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

サンプルのクラス

Page 14: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

型のバインド

ジェネリックメソッドの例

宣言側 仮型引数( type-parameter)

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)

利用側 実型引数( type-argument)

Collections.<String>replaceAll(list, "hoge", "piyo");

Page 15: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

型のバインド

ジェネリッククラスの例

宣言側 仮型引数( type-parameter)

public class ArrayList<E> {...}

利用側 実型引数( type-argument)

List<String> list = new ArrayList<String>();

Page 16: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

( 参考 ) 仮引数と実引数

メソッドの仮引数と実引数との対比

宣言側 仮引数( parameter)

public void hoge(int index){...}

利用側 実引数( argument)

hoge(123);

Page 17: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

型変数の境界

class Hoge<T extends B>

型変数の宣言時には境界を指定できる

new Hoge<A>(); ← NGnew Hoge<B>(); ← OKnew Hoge<B2>(); ← NGnew Hoge<C>(); ← OK

Page 18: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

型変数の境界

class Hoge <T extends A & Serializable>

& で繋いでインタフェースを境界に加えることができる

Page 19: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

中級編

Page 20: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

パラメータ化された型の代入互換性

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

→ コンパイルエラー

Page 21: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

異なる代入互換性

B の集合型 ArrayList<B> は

A の集合型 ArrayList<A> の

代理をできない

Page 22: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

ワイルドカードの使用

ArrayList<? extends B> の範囲

ArrayList<? super B> の範囲

Page 23: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

<? extends ~ > の入力制約List<? extends B> には add() できない List<C> 型を代入 B 型を add() とする List<C> に B 型が add() される ←矛盾

get() は B 型の戻り値を返す

Page 24: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

<? super ~ > の出力制約

ArrayList<? super B> にはB 型を add() できる

ただし get() は Object 型としてしか返せない

Page 25: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

継承によるバインド

public class StringList extends ArrayList<String> {}

というクラスを作ると

StringList list = new StringList();list.add("hoge");String str = list.get(0);

といったように、型変数のないクラスになる

Page 26: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

複雑になる前に

Map<String, Map<Integer, List<Hoge>>>

みたいにジェネリクスが複雑化するようなら適度なレベルでした継承クラスを作ることでシンプルでわかりやすくなる

クラスを作るのをサボらない

Page 27: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

上級編

Page 28: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰ジェネリクス

public abstract class Hoge<T extends Hoge<T>> {

public abstract T getConcreteObject();}

Hoge型の型変数 T は Hoge型を継承していなくてはならない

Page 29: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰する型変数へのバインド

new でバインドできない

Hoge<?> hoge = new Hoge<Hoge<...>>();

再帰ジェネリクスは継承で扱う

public class Piyo extends Hoge<Piyo> {...}

Page 30: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰ジェネリクスの効能

public class Piyo extends Hoge<Piyo> {@Overridepublic Piyo getConcreteObject() {return this;

}}

このようにすると

Piyo piyo = new Piyo();Piyo piyo2 = piyo.getConcreteObject();

Page 31: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰ジェネリクスの効能

public abstract class Hoge<T extends Hoge<T>> {

public abstract T getConcreteObject();}

親クラスで定義されたメソッドなのに子クラスの具象型を扱うことができる…!

Page 32: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰ジェネリクスの使用例

java.lang.Enum クラスの例

public abstract class Enum<E extends Enum<E>>

implements Comparable<E>, Serializable {

public final int compareTo(E o) {…}}

Page 33: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰ジェネリクスの使用例

enum SampleEnum {ONE,TWO,

}

に対して

SampleEnum.ONE.compareTo(SampleEnum.TWO);

は安全に compareToできる。他の enumと比較しようとするとコンパイルエラー

Page 34: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

内部クラスのジェネリクス

内部クラスは外部クラスの型変数を利用できる

public class Outer<T> {public class Inner {T genericField;

}}

Page 35: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

内部クラスの new の仕方

内部クラスの newの仕方知ってますか?

Outer<String> outer = new Outer<String>();Outer<String>.Inner inner = outer.new Inner();

内部クラスは外部クラスのインスタンスに紐づく「インスタンスの I/Oで型の関連性を示す」の I/Oには内部クラスも含まれます

Page 36: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

内部クラスの利用例

public class SampleList<T> extends ArrayList<T> {@Overridepublic Iterator<T> iterator() {

return super.iterator();}

public class SampleIteratorimplements Iterator<T> {// 略

}}

そもそも内部クラスの使いどころが難しいですが。

Page 37: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

new T() したい

「 Java のジェネリクスはnew T() ってできないのがクソだよね」

「お前の設計がクソなんじゃないの?」

Page 38: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

コンストラクタ

Java のオブジェクト指向

interface や親クラスとして振る舞うことが要求される

具象型の特有の情報を押し込める場所はコンストラクタ

Page 39: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

コンストラクタ

コンストラクタの引数の形は継承で制約できません

やりたければ Factory クラス作れ

Page 40: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

Factory の実装例

interface HogeFactory<T extends A> {/** デフォルトコンストラクタ的な */T newInstance();

}

インスタンスの生成に必要なデータを Factoryで制約

public class HogeTemplate {public <T> T template(HogeFactory<T> factory) {

return factory.newInstance();}

}

こうすればインスタンスの生成は可能になる、が面倒

Page 41: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

妥協例

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

}

Page 42: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

C# の例

class MyGenericClass<T> where T : new() {}

T 型にデフォルトコンストラクタがあることという制約ダサいけど妥当な妥協点か

Page 43: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

変態編

Page 44: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

再帰での相互参照

二つの型の具象型が、相互に相手の具象型を知っている

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>

Page 45: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

相互再帰+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>>

やりすぎです

Page 46: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

内部クラスでグルーピング

二つのクラスを囲うクラスを作って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();}

}やりすぎです

Page 47: ジェネリクスの基礎と応用 JJUG CCC 2012 Fall

まとめ型変数でメソッドやインスタンスの I/O の

型の関連性を表す→  まずは綺麗なオブジェクト指向の設計

文法をマスターするには3種類の<>を意識する

ジェネリクスが複雑になりすぎないように継承でのバインドを利用する

再帰ジェネリクスを応用すればサブクラスで具象型を扱える