Upload
nagise
View
5.494
Download
0
Embed Size (px)
DESCRIPTION
JJUG CCC 2012 Fall のセッション資料 #jjug_r12
Citation preview
ジェネリクスの基礎と応用
Twetter : @nagiseはてな : Nagise
所属 (株) Abby java-ja 北陸エンジニアグループ
ジェネリクスのスコープメソッドの中でのみ有効なジェネリクス
public static <T> void hoge(T t) { … }
インスタンスの中でのみ有効なジェネリク
ス
class Hoge<T> {
T t ;
...
}
ジェネリックメソッド
メソッドの引数と戻り値の型の関係を表すメソッドの複数の引
数の型の関係を表す
ジェネリックメソッドの例
java.util.Collections クラス
リスト内に出現する指定された値をすべてほかの値に置き換えます。
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
3つの引数が List<T>型、 T 型、 T 型という関係性
ジェネリックメソッドの例
java.util.Collections クラス
指定されたオブジェクトだけを格納している不変のセットを返します
public static <T> Set<T> singleton(T o)
引数が T 型で、戻り値が Set<T>型という関係性
ジェネリックメソッドの例
java.util.Collections クラス
デフォルトの乱数発生の元を使用して、指定されたリストの順序を無作為に入れ替えます。
public static void shuffle(List<?> list)
引数1つだけなので関連性を示す必要がない
ジェネリックメソッド
メソッドの引数と戻り値の型の関係を表すメソッドの複数の引
数の型の関係を表す
ジェネリックメソッドの呼び出し方
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);
インスタンスの I/O
複数のメソッド間の引数・戻り値の型の関係性公開されてい
るフィールド内部クラス
ジェネリックなインスタンスの例
java.util.ArrayListの例
public boolean add(E e)public E get(int index)
複数のメソッド間で型の関連性を表現している
文法のはなし
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;
}}似て非なる<>を色分けしてみました
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;
サンプルのクラス
型のバインド
ジェネリックメソッドの例
宣言側 仮型引数( type-parameter)
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
利用側 実型引数( type-argument)
Collections.<String>replaceAll(list, "hoge", "piyo");
型のバインド
ジェネリッククラスの例
宣言側 仮型引数( type-parameter)
public class ArrayList<E> {...}
利用側 実型引数( type-argument)
List<String> list = new ArrayList<String>();
( 参考 ) 仮引数と実引数
メソッドの仮引数と実引数との対比
宣言側 仮引数( parameter)
public void hoge(int index){...}
利用側 実引数( argument)
hoge(123);
型変数の境界
class Hoge<T extends B>
型変数の宣言時には境界を指定できる
new Hoge<A>(); ← NGnew Hoge<B>(); ← OKnew Hoge<B2>(); ← NGnew Hoge<C>(); ← OK
型変数の境界
class Hoge <T extends A & Serializable>
& で繋いでインタフェースを境界に加えることができる
中級編
パラメータ化された型の代入互換性
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());
→ コンパイルエラー
異なる代入互換性
B の集合型 ArrayList<B> は
A の集合型 ArrayList<A> の
代理をできない
ワイルドカードの使用
ArrayList<? extends B> の範囲
ArrayList<? super B> の範囲
<? extends ~ > の入力制約List<? extends B> には add() できない List<C> 型を代入 B 型を add() とする List<C> に B 型が add() される ←矛盾
get() は B 型の戻り値を返す
<? super ~ > の出力制約
ArrayList<? super B> にはB 型を add() できる
ただし get() は Object 型としてしか返せない
継承によるバインド
public class StringList extends ArrayList<String> {}
というクラスを作ると
StringList list = new StringList();list.add("hoge");String str = list.get(0);
といったように、型変数のないクラスになる
複雑になる前に
Map<String, Map<Integer, List<Hoge>>>
みたいにジェネリクスが複雑化するようなら適度なレベルでした継承クラスを作ることでシンプルでわかりやすくなる
クラスを作るのをサボらない
上級編
再帰ジェネリクス
public abstract class Hoge<T extends Hoge<T>> {
public abstract T getConcreteObject();}
Hoge型の型変数 T は Hoge型を継承していなくてはならない
再帰する型変数へのバインド
new でバインドできない
Hoge<?> hoge = new Hoge<Hoge<...>>();
再帰ジェネリクスは継承で扱う
public class Piyo extends Hoge<Piyo> {...}
再帰ジェネリクスの効能
public class Piyo extends Hoge<Piyo> {@Overridepublic Piyo getConcreteObject() {return this;
}}
このようにすると
Piyo piyo = new Piyo();Piyo piyo2 = piyo.getConcreteObject();
再帰ジェネリクスの効能
public abstract class Hoge<T extends Hoge<T>> {
public abstract T getConcreteObject();}
親クラスで定義されたメソッドなのに子クラスの具象型を扱うことができる…!
再帰ジェネリクスの使用例
java.lang.Enum クラスの例
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
public final int compareTo(E o) {…}}
再帰ジェネリクスの使用例
enum SampleEnum {ONE,TWO,
}
に対して
SampleEnum.ONE.compareTo(SampleEnum.TWO);
は安全に compareToできる。他の enumと比較しようとするとコンパイルエラー
内部クラスのジェネリクス
内部クラスは外部クラスの型変数を利用できる
public class Outer<T> {public class Inner {T genericField;
}}
内部クラスの new の仕方
内部クラスの newの仕方知ってますか?
Outer<String> outer = new Outer<String>();Outer<String>.Inner inner = outer.new Inner();
内部クラスは外部クラスのインスタンスに紐づく「インスタンスの I/Oで型の関連性を示す」の I/Oには内部クラスも含まれます
内部クラスの利用例
public class SampleList<T> extends ArrayList<T> {@Overridepublic Iterator<T> iterator() {
return super.iterator();}
public class SampleIteratorimplements Iterator<T> {// 略
}}
そもそも内部クラスの使いどころが難しいですが。
new T() したい
「 Java のジェネリクスはnew T() ってできないのがクソだよね」
「お前の設計がクソなんじゃないの?」
コンストラクタ
Java のオブジェクト指向
interface や親クラスとして振る舞うことが要求される
具象型の特有の情報を押し込める場所はコンストラクタ
コンストラクタ
コンストラクタの引数の形は継承で制約できません
やりたければ Factory クラス作れ
Factory の実装例
interface HogeFactory<T extends A> {/** デフォルトコンストラクタ的な */T newInstance();
}
インスタンスの生成に必要なデータを Factoryで制約
public class HogeTemplate {public <T> T template(HogeFactory<T> factory) {
return factory.newInstance();}
}
こうすればインスタンスの生成は可能になる、が面倒
妥協例
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);}
}
C# の例
class MyGenericClass<T> where T : new() {}
T 型にデフォルトコンストラクタがあることという制約ダサいけど妥当な妥協点か
変態編
再帰での相互参照
二つの型の具象型が、相互に相手の具象型を知っている
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>
相互再帰+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>>
やりすぎです
内部クラスでグルーピング
二つのクラスを囲うクラスを作って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();}
}やりすぎです
まとめ型変数でメソッドやインスタンスの I/O の
型の関連性を表す→ まずは綺麗なオブジェクト指向の設計
を
文法をマスターするには3種類の<>を意識する
ジェネリクスが複雑になりすぎないように継承でのバインドを利用する
再帰ジェネリクスを応用すればサブクラスで具象型を扱える