サーチ…


共分散

+記号は型パラメータを共変変数としてマークします。ここでは「 ProducerA上で共変です。

trait Producer[+A] {
  def produce: A
}

共変タイプのパラメータは、「出力」タイプと考えることができます。 Aを共変量としてマークAと、 Producer[X] <: Producer[Y]X <: Y提供していることがX <: Yます。例えば、 Producer[Cat]は有効なProducer[Animal]です。生産されたすべてのネコも有効な動物です。

共変型パラメータは、反変(入力)位置に現れません。私たちが主張しているとして、次の例では、コンパイルされませんCo[Cat] <: Co[Animal]が、 Co[Cat]def handle(a: Cat): Unitの任意扱うことができないAnimalで必要とされるCo[Animal]

trait Co[+A] {
  def produce: A
  def handle(a: A): Unit
}

この制限を扱う1つのアプローチは、共変タイプパラメータで囲まれた型パラメータを使用することです。次の例では、 BAスーパータイプであることがわかります。したがって、 Option[X]def getOrElse[B >: X](b: => B): BX任意のスーパータイプを受け入れることができるということをX <: Yに対してOption[X] <: Option[Y] - Option[Y]必要とされるYのスーパータイプを含む:

trait Option[+A] {
  def getOrElse[B >: A](b: => B): B
}

不変性

デフォルトでは、すべての型パラメータは不変です - 与えられたtrait A[B] 、 " AB上で不変です"と言います。これは、 A[Cat]A[Animal] 2つのパラメータを与えられた場合、 A[Cat] <: A[Animal]でもA[Cat] >: A[Animal] CatAnimal関係にかかわらず。

差異注釈は、このような関係を宣言する手段を提供し、関係が有効なままになるように型パラメータの使用に関する規則を課します。

コントラスト変動

-記号は反復型の型パラメータを示します。ここでは、 " HandlerA反変です"と言っています。

trait Handler[-A] {
  def handle(a: A): Unit
}

反変的型パラメータは、「入力」型と考えることができます。マーキングA反変そのアサートとしてHandler[X] <: Handler[Y]但しX >: Y 。例えば、 Handler[Animal]は有効なHandler[Cat]であり、 Handler[Animal]はcatsも処理する必要があるためです。

反変的型パラメータは、共変(出力)位置に現れません。次の例は、 Contra[Animal] <: Contra[Cat]宣言しているのでコンパイルされませんが、 Contra[Animal]def produce: Animal持っていますContra[Cat]必要に応じて猫を生成することは保証されていないdef produce: Animal

trait Contra[-A] {
   def handle(a: A): Unit
   def produce: A
}

ただし、解決策を過負荷にするために、反動は逆反復型パラメータの型の特異性を逆に反転させますHandler[Animal]Handler[Cat]より "より具体的"であるとみなされます。

型パラメータのメソッドをオーバーロードすることは不可能であるため、この動作は一般に暗黙の引数を解決する場合にのみ問題になります。次の例ではofCatの戻り値の型がより具体的なofAnimalofAnimalは使用されません。

implicit def ofAnimal: Handler[Animal] = ???
implicit def ofCat: Handler[Cat] = ???

implicitly[Handler[Cat]].handle(new Cat)

この動作は現在scala.math.Ordering 変更される予定であり、 scala.math.Orderingは型パラメータT不変である(例として)。 1つの回避策は、あなたのtypeclassを不変にすることと、指定された型のサブクラスに適用するイベントで暗黙の定義を型パラメータ化することです:

trait Person
object Person {
  implicit def ordering[A <: Person]: Ordering[A] = ???
}

コレクションの共分散

コレクションは通常、要素タイプ*で共変なので、スーパータイプが必要な場合は、サブタイプのコレクションを渡すことができます。

trait Animal { def name: String } 
case class Dog(name: String) extends Animal

object Animal {
  def printAnimalNames(animals: Seq[Animal]) = {
    animals.foreach(animal => println(animal.name))
  }
}

val myDogs: Seq[Dog] = Seq(Dog("Curly"), Dog("Larry"), Dog("Moe"))

Animal.printAnimalNames(myDogs)
// Curly
// Larry
// Moe

それは魔法のようには見えないかもしれませんが、 Seq[Animal]がその型パラメータで共変する高級型(ここではSeq )という概念全体をSeq[Dog]が受け入れるという事実は受け入れられます。

*反例は、標準ライブラリのセット

不変形質上の共分散

1つのメソッドが共変の引数を受け入れるようにする方法もあります。これは、あなたが反変的な位置でTを使用したいが、依然としてそれが共変しているので、必要かもしれない。

trait LocalVariance[T]{
  /// ??? throws a NotImplementedError
  def produce: T = ???
  // the implicit evidence provided by the compiler confirms that S is a
  // subtype of T.
  def handle[S](s: S)(implicit evidence: S <:< T) = {
    // and we can use the evidence to convert s into t.
    val t: T = evidence(s)
    ???
  }
}

trait A {}
trait B extends A {}

object Test {
  val lv = new LocalVariance[A] {}

  // now we can pass a B instead of an A.
  lv.handle(new B {})
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow