単純なコンテナを作る例
class Box(val value:Any)
- Anyはあらゆる型の頂点(Nullableを除く)
Anyであることが注目点
値を入れるのは簡単
val box: Box = Box("Hello")
何をしたいか
- あらゆる型に対応させたい(固定の方で定義したくない) VS キャストしたくない(Anyを使いたくない)
class Box<T>(val value: T)
val box: Box<String> = Box<String>("Hello")
不変・共変・反変
クラスと型
- 必ずしも一致しない
- 1つのクラスに複数の方
- 1つのクラスに無数の型
不変(invariant)
- サブタイプの関係が成り立たない
- デフォルトだとこれ
Box<Int>
にBox<Number>
を入れられない
共変(covariant)
- 型パラメータと同じサブタイピング関係が成り立つ
out
を使う
Box<out Number>
にBox<Int>
を入れられる
型プロジェクション
- 射影(projection)
- RDBにおいてはカラムの選択
- 型のある側面だけに注目
class MutableBox<T>(var value: T)
val box1: MutableBox<Int> = MutableBox<123>
val box2: MutableBox<out Number> = box1
box2.value = 0.5
- setterが削除されているから
- 型プロジェクションにより隠された
反変(contravariant)
- 型パラメータと逆のサブタイピング関係が成り立つ
in
を使う
fn setDefault(box; MutableBox<in Int>) {
box.value = 0
}
val box: MutableBox<Number> = MutableBox<NaN>
setDefault(box)
println(box.value)
まとめ
変位指定(宣言場所指定と型プロジェクション)
型プロジェクション
val box1: Box<Int> = MutableBox<123>
val box2: Box<out Number> = box1
宣言場所変位指定
class Box<out T>(val value: T)
class NumberBox<out T: Number>(val value: T) {
fn toInt(): NumberBox<Int> = NumberBox(value.toInt())
}
複数の上限境界
型消去とreified型
- コンパイルすると型が消える
- 型チェックはスタープロジェクションを使うと回避できる
具象型(reifiled type)パラメータ付き関数
メモ