具有覆盖抽象类型的蛋糕模式不适用于上部类型边界

时间:2012-04-18 16:05:34

标签: scala dependency-injection traits cake-pattern

我希望使用<:而非=覆盖特征中的抽象类型(如此处Scala Upper Bounds : value is not a member of type parameter回答)。

我想使用蛋糕模式,但这不起作用,我不明白为什么?

trait A {
  def ping = println("ping")
}

trait Cake {
  type T
}

trait S { this: Cake =>
  type T = A
  def t: T
  t.ping
}

好的,这个例子运行,但在我的实际用例中,我想用<:而不是=覆盖类型。似乎无法访问t函数,为什么?

trait S { this: Cake =>
  type T <: A
  def t: T
  t.ping
}

返回错误value ping is not a member of S.this.T

1 个答案:

答案 0 :(得分:15)

这是Scala类型系统的缺点。在确定mixin中的成员时,Scala使用两个规则:首先,具体总是覆盖抽象。第二,如果两个成员都是具体的,或者都是抽象的,那么稍后以线性化顺序出现的成员将获胜。

此外,特质的自我类型

trait S { this: C => ... }

隐式增加为

trait S { this: S with C => ... }

因此可以在S中访问特征S中的定义。在您的情况下,特征S被视为:

trait S { this: S with Cake =>
  type T = A
  def t: T
  t.ping
}

现在,只要T具体就可以了,因为它会覆盖Cake中的抽象T。但是如果T是抽象的,那么Cake中的那个在线性化顺序中出现并获胜。而且T没有上限,所以没有成员ping。解决此问题的一种方法是通过编写:

来更改线性化顺序
trait S { this: Cake with S =>
  type T <: A
  def t: T
  t.ping
}

如果Scala有一个不同的规则,即抽象类型成员的所有约束都在mixin中合并,而不是根据线性化顺序选择单个成员,那将更加清晰。这是我们将来要考虑的变化,但我们需要注意向后兼容性。