Черты Scala и структурные типы: может ли черта расширить структурный тип и затем назвать супер? - PullRequest
9 голосов
/ 02 марта 2012

Я хотел бы иметь черту, которая может а) смешиваться с любым классом с помощью определенного метода, и б) может вызывать супер. Примерно так:

  // A and B are from a library that I don't control.  No changes allowed here.
  class A {
    def stuff = "a stuff"
  }
  class B {
    def stuff = "b stuff"
  }

  // My code starts here

  type HasStuffMethod = {
    def stuff: String
  }

  // Note that this doesn't compile - gets:
  //   class type required but AnyRef{def stuff: String} found
  trait ImplementsStuff extends HasStuffMethod {
    override def stuff = "trait + " + super.stuff
  }

  val a = new A with ImplementsStuff
  assert(a.stuff == "trait + a stuff")

  val b = new B with ImplementsStuff
  assert(b.stuff == "trait + b stuff")

Есть ли способ сделать это?

Обратите внимание, что я не контролирую А и В; они приходят из другой библиотеки, которую я не могу изменить.

[Редактировать - добавлено после просмотра ответов]

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

  trait ImplementsStuff {
    this: HasStuffMethod =>
    abstract override def stuff = "foo" + "how do I call the original method here?"
  }

Это бесполезно, поскольку, когда вы смешиваете это во что-то, оно дает:

ошибка: переопределение метода в классе A типа => java.lang.String; Вещественный метод в свойстве ImplementsStuff типа => java.lang.String не может переопределить конкретный элемент без третьего участника, который отменяется обоими (это правило предназначено для предотвращения Переопределение '')

Но это не случайно; да, я действительно хочу, чтобы вы перешагнули через этот существующий метод. А потом позвольте мне это тоже назвать.

Ответы [ 3 ]

5 голосов
/ 03 марта 2012

Я могу придумать два варианта:

вариант 1, используя аннотацию собственного типа и вызывая stuff из нового метода (который я воображительно назвал callStuff) вместо его переопределения.

  trait ImplementsStuff  {
    this: HasStuffMethod =>
    def callStuff = "trait + " + this.stuff
  }

  val a = new A with ImplementsStuff
  assert(a.callStuff == "trait + a stuff")

  val b = new B with ImplementsStuff
  assert(b.callStuff == "trait + b stuff")

вариант 2 (поскольку вы говорите, что не управляете A и B) - это дорогой старый шаблон декоратора.

  trait HasStuff { def stuff: String }

  class DecorateStuff(decorated: HasStuffMethod) extends HasStuff {
    def stuff = "trait + " + decorated.stuff
  }
  val decA = new DecorateStuff(new A)
  assert(decA.stuff == "trait + a stuff")

  val decB = new DecorateStuff(new B)
  assert(decB.stuff == "trait + b stuff")
4 голосов
/ 02 марта 2012

Вам нужен abstract override в таком случае.

1 голос
/ 03 марта 2012

Нет способа расширить усовершенствованный тип в Scala (я не уверен, что это можно было бы безопасно выполнить, но было бы интересно исследовать).

У меня есть более подробное решение, которое приближает вас к цели.

trait HasStuff {
  def theirStuff: String
}

trait ImplementsStuff extends HasStuff {
  def stuff = "trait + " + theirStuff
}

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

val a = new A with ImplementsStuff {
  def theirStuff = super[A].stuff

  override def stuff = super[ImplementsStuff].stuff
}

val b = new B with ImplementsStuff {
  def theirStuff = super[B].stuff

  override def stuff = super[ImplementsStuff].stuff
}

На сайте mixin мне нужно реализовать супер-аксессор (theirStuff) для перенаправления на правильный унаследованный метод. Мне также нужно переопределить stuff на этом уровне, поскольку ImplementsStuff не наследует метод stuff, поэтому он не может override его.

Вызов stuff на a и b показывает, что он был переопределен:

$ a.stuff
res0: java.lang.String = trait + a stuff

$ b.stuff
res1: java.lang.String = trait + b stuff
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...