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

rey*_*n64 8 dependency-injection scala traits cake-pattern

我想覆盖trait中的抽象类型<:而不是=(在这里回答Scala Upper Bounds:value不是type参数的成员).

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

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

trait Cake {
  type T
}

trait S { this: Cake =>
  type T = A
  def t: T
  t.ping
}
Run Code Online (Sandbox Code Playgroud)

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

trait S { this: Cake =>
  type T <: A
  def t: T
  t.ping
}
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*sky 15

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

此外,自我类型的特征

trait S { this: C => ... }
Run Code Online (Sandbox Code Playgroud)

被隐含地增加到

trait S { this: S with C => ... }
Run Code Online (Sandbox Code Playgroud)

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

trait S { this: S with Cake =>
  type T = A
  def t: T
  t.ping
}
Run Code Online (Sandbox Code Playgroud)

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

trait S { this: Cake with S =>
  type T <: A
  def t: T
  t.ping
}
Run Code Online (Sandbox Code Playgroud)

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