Scala特质中超级trait的行为

4
为什么下面的 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)

更新:一个可能的解决方案如下。现在在AB中有相同的func1定义。如果我尝试将其移动到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)

还有一些澄清:在实际场景中,name是一个独立的方法,不能在func内联。此外,我正在尝试看看是否可以使所有扩展Base的特征的func定义在某种程度上相同。 - Mushtaq Ahmed
1个回答

12

我认为继承顺序可能是您正在寻找的顺序。如果您将" 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方法被trait 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中的legsDog中的legs是相同的,只是在Legs或者Dog中使用时不同而已... 同样地,如果你的对象是B,则def name将始终返回"B"

看起来你想将name用作私有方法:

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

我发现如果没有清晰的对象模型,使用trait和继承很快就会变得复杂。我假设您精简了示例并使用通用名称如A、B、Base、func,以便深入核心问题,但另一方面这并没有给我任何关于如何进行更改以使其适用于您的情况的见解。根据您的要求,我已经排列好代码,以便打印"B extends A extends Base"。我相信还有许多其他约束条件没有在问题中提到,这些约束条件可能是导致它不能为您工作的原因。

这只是名称始终为B。这就是问题的关键所在。为什么会这样?感谢您提出的解决方案,但我确实希望每个特征的func定义都相同。 - Mushtaq Ahmed
特性的超类调用在运行时解析。通过声明 trait A extends B,你表明 A 只能混入扩展自 B 的东西中。无法知道 A 是否混入了一个直接扩展 B 或在几个可堆叠的特性(...with A with B)之后才扩展B的类中。所以在特性声明的时候无法确定哪个特性将会“高于”这个具体修改或者换句话说,你的超级调用会引导到哪个修改。 - agilesteel
@huynhjl,请查看问题的更新。现在正在尝试消除func1的重复。 - Mushtaq Ahmed
@MushtaqAhmed,我没有看到你在更新的问题中采纳了我的其他建议。因此,我不确定是否还有其他好的想法,因为我没有足够的上下文和洞察力来了解你所面临的限制。 - huynhjl
@huynhjl 我无法将名称设置为私有成员,因此无法使用该方法。但是你是对的,也许在没有太多上下文的情况下很难抽象出来。 - Mushtaq Ahmed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接