以类型安全的方式将Foo列表[T<:Foo]连接到Foo的子类

时间:2015-08-31 12:37:03

标签: scala

class Foo {}

class Bar[T <: Foo] {
    var items : List[T] = Nil
    def add(i: T) = items ::= i
}

class Fog extends Foo {
    def add(bar: Bar[this.type]) = bar.add(this)
}

object Test {
    val bag = new Bar[Fog]
    val fog = new Fog
    fog.add(bag)
}

编译器咆哮着:

Error:(20, 13) type mismatch;
 found   : Bar[rules.Fog]
 required: Bar[rules.Test.fog.type]
Note: Fog >: Test.fog.type, but class Bar is invariant in type T.
You may wish to define T as -T instead. (SLS 4.5)
    fog.add(bag)
            ^

但是,将T转换为-T会产生:

Error:(9, 9) contravariant type T occurs in covariant position in type => List[T] of method items
    var items : List[T] = Nil
        ^

我想要实现的是以类型安全的方式添加到Bar [Fog]。

1 个答案:

答案 0 :(得分:0)

您的代码暗示您想要继承Fog,是吗?

如果没有,你可以这样做:

final class Fog extends Foo {
  def add(bar: Bar[Fog]) = bar.add(this)
}

如果是,那么您将不得不重新安排您的代码,因为add需要Fog的任何实现包(例如子类或new Fog with Mixin):

class Foo {}

class Bar[T <: Foo] {
  var items : List[T] = Nil
  def add(i: T) = items ::= i
}

class Fog extends Foo {
  def add(bar: Bar[this.type]) = bar.add(this)
}

trait Mixin {
   def crazy = true
}

object Test {
  val fog = new Fog with Mixin
  // you need to tell Bar what fog actually is
  val bag = new Bar[fog.type]
  fog.add(bag)
}

修改

但是,这意味着您只能将Bar定义的确切类型添加到bag

根据您的需要,您还可以在T contra-variant 中制作Bar,然后您可以在包中添加Fog的不同子类型:

class Foo {}

class Bar[-T <: Foo] {
  var items : List[Foo] = Nil
  def add[A <: T] (i: A) = items ::= i
}

class Fog extends Foo {
  def add(bar: Bar[Fog]) = bar.add(this)
}

trait Mixin {
  def crazy = true
}

val fog = new Fog with Mixin
// you need to tell Bar what fog actually is
val bag = new Bar[Fog]
fog.add(bag)
(new Fog).add(bag)

// when accessing items you would have to type match, though
val test1 = bag.items.map {
  case f : Fog with Mixin => 1
  case f : Fog => 2
  case f : Foo => 3
}

// Result: List(2, 1)