【Scala】 変位アノテーション「非変」「共変」「反変」
環境 Scala 2.11.6 CentOS7.0
「Scalaスケーラブルプログラミング」(コップ本)メモ
■ 変位アノテーション(第19章)
例えば
class Foo(foo: AnyRef){}
というクラスがあったとき、引数fooにはAnyRefとそのサブ型は全て受け入れられる。
scala> val f1 = new Foo("anyref":AnyRef) f1: Foo = Foo@4d339552 scala> val f2 = new Foo("string":String) f2: Foo = Foo@52aa2946 // AnyRefのサブ型でないものは勿論NG scala> val f3 = new Foo(1:Int) <console>:8: error: the result type of an implicit conversion must be more specific than AnyRef val f3 = new Foo(1:Int)
では型パラメータ付きの場合はどうなるか。
class Item[T](elem: T) {} class Bar(item: Item[AnyRef]) { def show = { this.item } }
というクラスがあるときに、明示的にBarの引数となる Item[T] の T がT->AnyRef
のようにAnyRefサブ型となるパラメータを与えてみる。
scala> new Bar(new Item[AnyRef]("anyref")) res0: Bar = Bar@ba4d54 scala> new Bar(new Item[String]("string")) <console>:10: error: type mismatch; found : Item[String] required: Item[AnyRef] Note: String <: AnyRef, but class Item is invariant in type T. You may wish to define T as +T instead. (SLS 4.5) new Bar(new Item[String]("string")) ^
感覚的には、Item[String] -> Item[AnyRef]
のようにサブ型になっている気がするが、Scalaではこれが暗黙的には認められない。
これを非変(厳格)という。
ただし型を明示的に示さない場合、暗黙的に型変換が可能であれば、変換される。
// Stringは暗黙的にAnyRefに変換される scala> val b = new Bar(new Item("string")) b: Bar = Bar@13805618 scala> b.show res3: Item[AnyRef] = Item@335eadca // IntはAnyRefに暗黙的に変換できない scala> new Bar(new Item(1)) <console>:10: error: the result type of an implicit conversion must be more specific than AnyRef new Bar(new Item(1))
ここから本題の「変位アノテーション」について。
型パラメータがあるときに、型パラメータ付きの型に明示的に、サブ型関係を与えたいとき。
つまり
Item[String] -> Item[AnyRef]
としたいときは以下のようにする。
▽ 共変
class Item2[+T](elem: T) {}
T -> S
のようTがSのサブ型であるとき
Item[T] -> Item[S]
、Item[T]がItem[S]のサブ型になる。
class Item2[+T](elem: T) extends Item[T]{} class Other2(item: Item2[Any]) { def show= { this.item } } // 明示的にStringを指定しても問題ない scala> new Other2(new Item2[String]("string")) res2: Other2 = Other2@11a9e7c8
▽ 反変
class Item3[-T](elem: T) {}
T -> S
のようTがSのサブ型であるとき
Item[S] -> Item[T]
、Item[S]がItem[T]のサブ型になる。
class Item3[-T](elem: T) {} class Other3(item: Item3[AnyRef]) { def show= { this.item } } // String -> AnyRef なので、Item3[AnyRef] は Item3[String]のサブ型として定義されている scala> new Other3(new Item3[String]("string")) <console>:10: error: type mismatch; found : Item3[String] required: Item3[AnyRef] new Other3(new Item3[String]("string")) ^ // AnyRef -> Any なので、Item3[Any] は Item3[AnyRef]のサブ型として定義されている scala> new Other3(new Item3[Any]("string")) res4: Other3 = Other3@1cbbffcd