object Test {
trait Foo
trait Bar {
def someMethod(): Unit
val someValue: String
}
trait Runner {
protected type A <: Foo
def run(a: A): Unit
}
trait Runner2 {
this: Runner =>
protected type A <: Foo with Bar
def run(a: A): Unit = a.someMethod()
}
}
在上面的示例中,如果Runner2 不自行输入为Runner,则代码会编译,但如果是,则不会:
Error:(42, 29) value someMethod is not a member of Runner2.this.A
def run(a: A): Unit = a.someMethod()
我理解这是因为自我输入导致Runner.A比Runner2的覆盖“优先”,但是 - 为什么?
如果我不使用自我键入,则Runner2的具体实现会出现其他问题,因为此类实现会扩展Runner和混合Runner2。
处理这种关系的最佳方式是什么?
答案 0 :(得分:0)
我认为在这种情况下,答案就在你面前......; - )
错误说明:value someMethod is not a member of Runner2.this.A
。
您已声明Runner2.this
属于Runner
类型,A
- 如特性Runner
中所定义 - 没有名为someMethod
的方法。因此编译错误。
当您删除自我键入时,它会进行编译,因为现在Runner2
的类型A
(类型Foo with Bar
)确实有someMethod
。
自我键入本质上是一种声明,在使用之前,相关的特征将与指定的类型(或该类型的子类)混合在一起。如果您尝试使用Runner2
的实例(使用自我类型声明)而不将Runner
的实例与其混合使用,则会出现编译器错误。
例如:
abstract class X extends Runner2
将无法编译,因为Runner
未混入。(error: illegal inheritance; self-type X does not conform to Runner2's selftype Runner2 with Runner
。)但是,这会起作用(原则上,还有其他问题 - 见下文):
abstract class X extends Runner2 with Runner
自我键入的作用是让您访问指定类型的元素。这对于在 Scala 中处理依赖注入的方法之一至关重要。 (有关详细信息,请参阅Daniel Sobral's answer to this question。)
但是Runner
扩展Runner2
而不是相反,所以你也会发现编译器会抱怨Runner
对A
的定义具有不兼容的类型(因为Foo
不是Foo with Bar
的子类)。