ぶろぐ

日記です

共変 Covariant


ジェネリックを緩くする

//こういうメソッドがあるとして
scala> def pagerPrinter(pager: Pager[Any]) { pager.list foreach println }
pagerPrinter: (pager: Pager[Any])Unit

//ジェネリックでクラスを定義
scala> class Pager[T](val list:Seq[T])
defined class Pager

scala> val pager = new Pager[String]( List("hoge", "foo") )
pager: Pager[String] = Pager@66cc3c53

//共変を許容していないのでエラー
scala> pagerPrinter(pager)
<console>:11: error: type mismatch;
 found   : Pager[String]
 required: Pager[Any]
Note: String <: Any, but class Pager is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
              pagerPrinter(pager)

//共変を許容
scala> class PagerCovariant[+T](val list:Seq[T])
defined class Pager

scala> val pagerConvariant = new Pager[String]( List("hoge", "foo") )
pagerConvariant: Pager[String] = Pager@65e719a0

//おけー
scala> pagerPrinter(pagerConvariant)
hoge
foo

あれ、ちょっとわからなくなってきた。そもそもジェネリック使わなければおkな話では・・・?

scala> class Pager(val list:Seq[Any])
scala> val pager = new Pager( List("hoge", "foo") )
scala> def pagerPrinter(pager: Pager) { pager.list foreach println }
pagerPrinter: (pager: Pager)Unit

scala> pagerPrinter(pager)
hoge
foo

そうだそうだ、ジェネリック使わないと型をキャストしたりしないといけなくなるんだ。でも、型がなんであってもいいような操作をするメソッドを通したいときのために共変に。

追記:上記本質じゃなかった

型パラメーターにAnyを指定していたのが良くなかった。関数側で型パラメーターに縛りが必要ない場合は_を使えばOKだった。
scala> def pagerPrinter(pager: Pager[_]) { pager.list foreach println }
共変はサブクラスを許容する場合に使う。