Подтвердить что ты не робот

Можно ли вызвать переопределенный метод из собственного типа?

Рассмотрим это:

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

Я был приятно удивлен, узнав, что это возможно, и работает так, как ожидалось:

new Foo with Bar foo 

возвращает "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 и т.д.), Но без везения.

Это просто невозможно?

4b9b3361

Ответ 1

Нет. Невозможно вызвать переопределенный метод из типа self.

Во-первых, признак Bar не является преемником класса Foo, поэтому невозможно использовать super.foo.

А во-вторых, также невозможно использовать self.foo, поскольку self имеет тип 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)
    }
  }
}

Итак, с помощью self.foo вы пытаетесь получить доступ к методу Foo признака Bar. Такое поведение соответствует Scala Спецификация (PDF):

Последовательность операторов шаблона может иметь префикс с формальным определение параметра и стрелка, например. x = > , или x: T = > . Если формальный параметр указан, его можно использовать как псевдоним для ссылки на этот по всему телу шаблона. Если появляется формальный параметр с типом T это определение влияет на тип S типа базовый класс или объект следующим образом: пусть C - тип класса или признак или объект, определяющий шаблон. Если тип T задан для формальный собственный параметр, S - наибольшая нижняя граница T и C. Если нет тип T задается, S - всего C. Внутри шаблона тип этого предполагается, что S.

Можно получить доступ к методу с использованием отражения, но я думаю, что это не то, что вы ищете.

Ответ 2

Я не знаю какого-либо конкретного синтаксиса, чтобы разделить базовый класс и смешанный признак. Однако есть простое решение для достижения результата вручную путем выделения переопределенного метода из реализации по умолчанию в базовом классе:

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

Как и ожидалось

new Foo with Bar foo == "foobar"
new Foo foo == "foo"

Ответ 3

Вы увеличиваете свой признак Foo вместо использования типа self:

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

См. также этот ответ.