Scala型パラメータ
はじめに
- クラス、インタフェース定義時に何の型が入るか決まっていないがインスタンス化する際に型を決定すること
例) クラス定義時に何の型が入るか不明であるため、型引数としてTを定義
class Glass[T](var content: T) {
def put(t: T): Unit = { content = t }
def get: T = content
}
型を定義する
class Drink
class Juice extends Drink
class Water extends Drink
インスタンス化するときに、型を決定する
val waterGlass = new Glass[Water](new Water)
val juiceGlass = new Glass[Juice](new Juice)
サブタイプ
- スーパークラスにサブクラスのインスタンスをバインドできる機能のこと
例)
val drink: Drink = new Juice
非変
- 型パラメータ
Glass[T]
は、デフォルトの状態では、サブタイプの関係が存在しない - つまり
Water
がDrink
のサブタイプであっても、Glass[Water]
型をGlass[Drink]
型にバインドできない
// コンパイルエラー
val glassDrink: Glass[Drink] = new Glass[Water](new Water)
これを非変といい、Scalaではデフォルト
共変
- 型パラメータで定義したクラスにもサブタイプの関係を利用したい場合もある
- その場合共変[+T]することで、サブタイプの関係を型パラメータで定義したクラスに使うことができる
class Glass[+T](var content: T) {
def put(t: T): Unit = { content = t }
def get: T = content
}
val glassDrink: Glass[Drink] = new Glass[Water](new Water)
反変
- 共変の逆
val glassDrink: Glass[Water] = new Glass[Drink](new Drink)
変異指定
- サブタイプの関係を、型パラメータのクラスで指定する制約のこと
- ボトムタイプの
Nothing
型を全ての型にバインドできるAny └─AnyVal | AnyRef └─Unit | └─String └─Int | └─class └─Long | └─Null └─Double | └─Byte | └─etc... | └─ └─ Nothing
例)Listは共変
val list: List[Any] = List[Int](1,2,3)
List[Int]
をList[Any]
にバインドできる
下限境界
- BはAのスーパー型
- 結果型
List[B]
は、Listがインスタンス化した要素を返すか、汎化した要素のListを返すことを表している
sealed abstract class List[+A] extends AbstractSeq[A]
// 省略
def ::[B >: A](x: B): List[B] = new scala.collection.immutable.::(x, this)
例)同じ要素(Int
)の場合には、同じ要素(Int
)のList
型が作成される
scala> val list = List(2,3,4)
list: List[Int] = List(2,3,4)
scala> val listInt = 1 :: list
listInt: List[Int] = List(1,2,3,4)
例)Double
型を要素に追加する場合は、Int、Double
のスーパータイプであるAnyVal
型のList
が作られる
scala> val listAnyVal = 0.5 :: listInt
listAnyVal: List[AnyVal] = List(0.5,1,2,3,4)
例)String
型を要素に追加する場合は、Int、String
のスーパータイプであるAny
型のList
が作られる
scala> val listAny = "Hello" :: listInt
listAny: List[Any] = List(Hello,1,2,3,4)
- 通常引数の位置に共変型パラメータが入ると型安全が壊れる可能性があり、このまま型パラメータAを引数に持っていくとコンパイラに怒られる
- しかし、immutableなクラスであれば型は壊れない
通常引数に共変型パラメータが入ると型安全が壊れる理由
- 非変で定義された
Arrya[T]
は、mutableなコレクション(配列)
例)仮に共変がOKだった場合に、Water
型がJuice
型に変わってしまう例
val arrayWater: Array[Water] = Array(Water,Water)
val arrayDrink: Array[Drink] = arrayWater
arrayDrink(0) = Juice
反変のメリット
- FunctionN(関数を定義するtraitで、通常はシンタックスシュガーを使う)
- TODO