Поведение супер в цепных чертах Scala - PullRequest
4 голосов
/ 08 октября 2011

Почему x.func ниже возвращает "B extends B extends B"? Как расположить этот код так, чтобы он возвращал "B extends A extends Base"?

trait Base {
  def name = "Base"
  def func = name
}

trait A extends Base {
  override def name = "A"
  override def func = name + " extends " + super.func
}

trait B extends Base {
  override def name = "B"
  override def func = name + " extends " + super.func
}

val x = new Base with A with B
println(x.func)

Обновление: одна договоренность может быть следующей. Теперь он имеет идентичные определения func1 в A и B. Это не работает, если я пытаюсь переместить его в класс Derived. Есть идеи как убрать повтор func1?

trait Base {
  def name = "Base"
  def func1(s: String) = s
}

trait Derived extends Base {
  def func = func1(name)
}

trait A extends Derived {
  override def func1(s: String) = s + " extends " + super.func1(super.name)
  override def name = "A"
}

trait B extends Derived {
  override def func1(s: String) = s + " extends " + super.func1(super.name)
  override def name = "B"
}

val x = new Base with A with B
println(x.func)

1 Ответ

11 голосов
/ 08 октября 2011

Я думаю, что порядок наследования может быть тем, который вы ищете. Если вы замените " extends " на тот, который показывает, какой метод какой черты называется:

trait Base {
  def name = "Base"
  def func = "Base." + name
}

trait A extends Base {
  override def name = "A"
  override def func = name + " A.extends " + super.func
}

trait B extends Base {
  override def name = "B"
  override def func = name + " B.extends " + super.func
}

val x = new Base with A with B
println(x.func)
// B B.extends B A.extends Base.B

Просто name всегда "B". Другими словами:

trait Base { def func = "Base" } 
trait A extends Base { override def func = "A extends " + super.func }
trait B extends Base { override def func = "B extends " + super.func }
val x = new Base with A with B
println(x.func)
// B extends A extends Base

что ты и хочешь ...

Полная линеаризация вашего примера:

Object, B, A, Base, ScalaObject, AnyRef, Any

(см. http://ofps.oreilly.com/titles/9780596155957/ScalaObjectSystem.html#Linearization для практического объяснения того, как вычислить линеаризацию)

Изменить, чтобы ответить на комментарий: Почему name всегда возвращает "B"? Это потому, что метод def name переопределяется чертой B для возврата "B". В этом весь смысл наследования, чтобы иметь возможность использовать поведение суперклассов, которое уточняется в подклассах:

trait Legs { 
  def legs: Int 
  def printLegs() { println("I have " + legs + " legs") }
}

class Dog extends Legs { def legs = 4 }
class Chicken extends Legs { def legs = 2 }

new Dog printLegs
// I have 4 legs
new Chicken printLegs
// I have 2 legs

legs в признаке Legs не является отдельным от legs в Dog в зависимости от того, ссылаетесь ли вы на него в Legs или Dog ... Аналогично, ваш def name всегда будет верните "B", если ваш объект B.

Похоже, вы хотите использовать имя в качестве частного метода:

trait Base { 
  private def _name() = "Base"
  def func = _name
} 
trait A extends Base { 
  private def _name() = "A"
  override def func = _name + " extends " + super.func
}
trait B extends Base { 
  private def _name() = "B"
  override def func = _name + " extends " + super.func
}
val x = new Base with A with B
println(x.func)
// B extends A extends Base

Я считаю, что если нет четкой объектной модели, использование признаков и наследования быстро усложняется. Я предполагаю, что вы продезинфицировали / упростили пример, чтобы использовать общие имена, такие как A, B, Base, func, чтобы вы поняли суть проблемы, но с другой стороны, это не дает мне никакой информации о том, какие изменения вы могли бы сделать, чтобы заставить его работать для тебя. Как вы и просили, я расположил код так, чтобы он печатал "B extends A extends Base". Я уверен, что есть ряд других ограничений, которые не в том, почему это не будет работать для вас.

...