类型,自我键入和类型别名

时间:2017-05-19 17:43:41

标签: scala

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。

处理这种关系的最佳方式是什么?

1 个答案:

答案 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而不是相反,所以你也会发现编译器会抱怨RunnerA的定义具有不兼容的类型(因为Foo不是Foo with Bar的子类)。