読者です 読者をやめる 読者になる 読者になる

takafumi blog

日々の勉強メモ

【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