Kotlinの総称型(ジェネリクス) 後編
今回の記事は前回に引き続き、ことりんの総称型についてです。
Javaの問題点を克服した造りになっており非常に便利に感じます。
※下記URLのサイトを参考にしました。英語、または技術的な知識が至らず、内容に誤りが含まれるおそれがありますので、ご了承ください。
※本エントリは参考サイトの翻訳をベースに、加筆・変更を施した構成となっています。
参考サイト http://confluence.jetbrains.net/display/Kotlin/Generics
ジェネリック関数
クラスだけではなく、関数も型パラメータを持てます。通常、型パラメータは関数名の後に山括弧(<>←これ)の中に置けます。
fun foo<T>(t : T) : T { //・・・ }
しかし拡張関数では、レシーバの型の指定の前に型パラメータを指定する必要があります。Kotlinではもうひとつの構文も許可しています。
fun <T> Int.foo(t : T) : T { //・・・ }
明示的に型パラメータを渡したい場合、関数名の後にそれを指定して呼び出します。
val str = foo<String>("abc")
ジェネリック制約
与えられた型パラメータの代用となり得る型の集合は、ジェネリック制約によって制限されることがあります。
上限
最も一般的な制約は、Javaのextendsキーワードに対応する上限(upper bound)です。
fun sort<T : Comparable<T>>(list : List<T>) { // ... }http://confluence.jetbrains.net/display/Kotlin/Generics より引用
コロンの後に指定されている型が上限です。つまりTの代わりになれるのはComparable
sort(list(1, 2, 3)) // OK. Int is a subtype of Comparable<Int> sort(list(HashMap<Int, String>())) // Error: HashMap<Int, String> is not a subtype of Comparable<HashMap<Int, String>>http://confluence.jetbrains.net/display/Kotlin/Generics より引用
デフォルト(何も指定されていないとき)の上限は Any? です。山括弧の中に複数の型パラメータは指定できません。同一型パラメータが複数の上限を必要とする場合、where節で区切ります。
fun cloneWhenGreater<T : Comparable<T>>(list : List<T>, threshold : T) : List<T> where T : Cloneable { return list when {it > threshold} map {it.clone()} }http://confluence.jetbrains.net/display/Kotlin/Generics より引用
クラスオブジェクト
その他のジェネリック制約は、クラスオブジェクトの制約です。それは、Tの代わりになる型のルートクラスのクラスオブジェクトのプロパティを制限します。次の例を考えてください。Defaultクラスがあると仮定します。このクラスは、自身のデフォルト値を保持するdefaultプロパティを持っています。
abstract class Default<T> { val default : T }http://confluence.jetbrains.net/display/Kotlin/Generics より引用
例えばIntクラスは次のようにDefaultクラスを拡張できるでしょう。
class Int { class object : Default<Int> { override val default = 0 } // ... }http://confluence.jetbrains.net/display/Kotlin/Generics より引用
では、複数のnull許容なT(つまり T?)を取り、すべてのnullをデフォルト値に置換するような関数を考えましょう。
fun replaceNullsWithDefaults<T : Any>(list : List<T?>) : List<T> { return list map { if (it == null) T.default // For now, we don't know if T's class object has such a property else it } }http://confluence.jetbrains.net/display/Kotlin/Generics より引用
この関数をコンパイルするためには、TのクラスオブジェクトがDefault
fun replaceNullsWithDefaults<T : Any>(list : List<T?>) : List<T> where class object T : Default<T> { // ...http://confluence.jetbrains.net/display/Kotlin/Generics より引用
今、コンパイラはTが(クラスオブジェクトの参照として)defaultプロパティを持っていることを知っているので、アクセスが出来るようになります。Kotlinの総称型は実行時に保持されるので、これが可能となります。
実行時型情報
Kotlinは実行時に、型や関数のジェネリック引数を含む型情報を保存します。
この情報は typeinfo()関数によって取得できます。
val typeinfo = typeinfo(list)http://confluence.jetbrains.net/display/Kotlin/Runtime+Type+Information より引用
typeinfo()は 型情報と、与えられた型のメンバへ自己反映的なアクセスを提供するTypeInfoクラスのオブジェクトを返します。
typeinfo()関数のオーバロードバージョンを使えますが、型引数に渡します。
val typeinfo = typeinfo<List<Int>>http://confluence.jetbrains.net/display/Kotlin/Runtime+Type+Information より引用