是否可以从自我类型调用重写方法?

Dim*_*ima 15 scala

考虑一下:

 class Foo { def foo = "foo" }
 trait Bar { self: Foo =>
    override def foo = "bar"
 }
Run Code Online (Sandbox Code Playgroud)

我惊喜地发现这是可能的,并按预期工作:

new Foo with Bar foo 
Run Code Online (Sandbox Code Playgroud)

返回"bar".问题是,是否可以Bar.foo调用Foo.foo,就像在"普通"继承案例中经常做的那样. override def foo = super.foo + "bar"不起作用(说"foo不是AnyRef的成员),也没有override def foo = self.foo + "bar"(它最终只是调用自身,并导致无限递归).我尝试了一些其他组合(如self.Foo.foo,Foo.this.foo等),但没有任何运气.

这不可能吗?

dko*_*kov 5

不。不可能从 self 类型调用重写的方法。

首先,该 traitBar不是 class 的继承者,Foo因此不可能使用super.foo.

其次,也不能使用self.foo因为self实际上是 type Bar with Foo。可以通过在以下之后打印程序来显示typer

$ scalac -Xprint:typer test.scala
[[syntax trees at end of                     typer]] // test.scala
package <empty> {
  class Foo extends scala.AnyRef {
    def <init>(): Foo = {
      Foo.super.<init>();
      ()
    };
    def foo: String = "foo"
  };
  abstract trait Bar extends scala.AnyRef { self: Bar with Foo => 
    def /*Bar*/$init$(): Unit = {
      ()
    };
    override def foo: String = "bar"
  };
  class FooBar extends Foo with Bar {
    def <init>(): FooBar = {
      FooBar.super.<init>();
      ()
    }
  };
  object TestApp extends scala.AnyRef {
    def <init>(): TestApp.type = {
      TestApp.super.<init>();
      ()
    };
    def main(args: Array[String]): Unit = {
      val a: FooBar = new FooBar();
      scala.this.Predef.println(a.foo)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,self.foo您正在尝试访问footrait的方法Bar。这种行为符合Scala 规范(PDF):

模板语句序列可以以形式参数定义和箭头作为前缀,例如 x => 或 x: T =>。如果给出了形式参数,则它可以用作整个模板正文中引用 this 的别名。如果形式参数带有类型 T,则此定义会影响底层类或对象的 self 类型 S,如下所示:设 C 为定义模板的类或特征或对象的类型。如果为形式 self 参数给出了类型 T,则 S 是 T 和 C 的最大下界。如果没有给出类型 T,则 S 就是 C。在模板内部,假设 this 的类型为 S。

可以使用反射访问该方法,但我认为这不是您想要的。


Dan*_*elM 0

我不知道有任何特定的语法可以解开基类和混合特征。然而,有一个简单的解决方案可以通过将重写的方法与基类中的默认实现区分开来手动实现结果:

class Foo { def foo = defaultFoo; def defaultFoo = "foo" }
trait Bar { self: Foo => override def foo = self.defaultFoo + "bar" }
Run Code Online (Sandbox Code Playgroud)

正如预期的那样

new Foo with Bar foo == "foobar"
new Foo foo == "foo"
Run Code Online (Sandbox Code Playgroud)