Is it possible to call an overridden method from self type?

后端 未结 3 613
独厮守ぢ
独厮守ぢ 2021-01-07 23:32

Consider this:

 class Foo { def foo = \"foo\" }
 trait Bar { self: Foo =>
    override def foo = \"bar\"
 }

I was pleasantly surprised

相关标签:
3条回答
  • 2021-01-08 00:23

    No. It is impossible to call overridden method from a self type.

    Firstly the trait Bar is not a successor of class Foo so it is not possible using super.foo.

    And secondly it is also not possible using self.foo since self is actually of type Bar with Foo. It can be shown by printing the program after 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)
        }
      }
    }
    

    So with self.foo you are trying to access the method foo of the trait Bar. Such behavior matches the Scala Specification (PDF):

    The sequence of template statements may be prefixed with a formal parameter definition and an arrow, e.g. x =>, or x: T =>. If a formal parameter is given, it can be used as an alias for the reference this throughout the body of the template. If the formal parameter comes with a type T, this definition affects the self type S of the underlying class or object as follows: Let C be the type of the class or trait or object defining the template. If a type T is given for the formal self parameter, S is the greatest lower bound of T and C. If no type T is given, S is just C. Inside the template, the type of this is assumed to be S.

    It is possible to access the method using reflection but I think that it is not what you are looking for.

    0 讨论(0)
  • 2021-01-08 00:33

    I am not aware of any particular syntax to disentangle the base class and the mixed-in trait. There is, however, an easy solution to achieve the result manually by distinguishing the overridden method from the default implementation in the base class:

    class Foo { def foo = defaultFoo; def defaultFoo = "foo" }
    trait Bar { self: Foo => override def foo = self.defaultFoo + "bar" }
    

    As expected

    new Foo with Bar foo == "foobar"
    new Foo foo == "foo"
    
    0 讨论(0)
  • 2021-01-08 00:39

    You make your trait extend Foo instead of using the self type:

    class Foo {def foo = "foo"}
    trait Bar extends Foo {
      override def foo = super.foo + "bar"
    }
    new Foo with Bar foo // barfoo
    

    See also this answer.

    0 讨论(0)
提交回复
热议问题