Scala 与冲突成员混合的Trait:为什么我的代码可以成功编译?
对不起,这个问题可能有点长,因为我想尽可能准确地描述问题和我的理解 最近我在学习Scala的特质系统 我做了一些关于冲突成员的实验,请参见以下代码:Scala 与冲突成员混合的Trait:为什么我的代码可以成功编译?,scala,oop,Scala,Oop,对不起,这个问题可能有点长,因为我想尽可能准确地描述问题和我的理解 最近我在学习Scala的特质系统 我做了一些关于冲突成员的实验,请参见以下代码: trait TA { def play() = println("TA play") } trait TB { def play() = println("TB play") } trait TC { def play() = println("TC play") } c
trait TA {
def play() = println("TA play")
}
trait TB {
def play() = println("TB play")
}
trait TC {
def play() = println("TC play")
}
class MyClass extends TA with TB with TC {
}
当然,这段代码编译失败了:
Error:(13, 8) class MyClass inherits conflicting members:
method play in trait TB of type ()Unit and
method play in trait TC of type ()Unit
(Note: this can be resolved by declaring an override in class MyClass.)
class MyClass extends TA with TB with TC {
^
我的理解是
类MyClass
的线性化是{MyClass,TC,TB,TA}
,但是由于TB的play
和TC
的play
上没有覆盖
,因此编译失败
一种解决方法是在TA
、TB
、TC
上标记override
,如下所示:
trait IPlay {
def play()
}
trait TA extends IPlay {
override def play() = println("TA play")
}
trait TB extends IPlay {
override def play() = println("TB play")
}
trait TC extends IPlay {
override def play() = println("TC play")
}
class MyClass extends TA with TB with TC {
}
好的,这段代码可以按预期成功编译
但是这个呢:
trait TA {
def play() = println("TA play")
}
trait TB {
def play() = println("TB play")
}
trait TC {
def play() = println("TC play")
}
class MyClass extends TA with TB with TC {
override def play(): Unit = {
println("MyClass play")
super.play()
super[TC].play()
super[TA].play()
super[TB].play()
}
}
(new MyClass).play()
// -- Output:
// MyClass play
// TC play
// TC play
// TA play
// TB play
令我惊讶的是,这段代码也可以成功编译
classMyClass
的线性化仍然是{MyClass,TC,TB,TA}
,在TB
的play
和TC
的play
上没有override
,唯一的区别是我在MyClass
的play
上添加了override
为什么可以成功编译
请注意,Scala不是Java或C#,没有冲突成员的默认行为
在Java中,如果不将方法标记为@Override
,则默认行为为Override
在C#中,如果不将方法标记为覆盖
,则默认行为是隐藏
在Scala中,如果您没有将方法标记为覆盖
,则没有默认行为,它们应该是冲突成员,IMO
因此,我认为上面的代码编译应该失败,但为什么它可以成功呢
或者我的理解是错误的
非常感谢。您已经在子类中手动定义了行为。
你为什么感到惊讶?在这种情况下,什么是不正确的
我认为这个例子中最让人困惑的地方是“super.play()”是合法的,并且与最后一个继承的成员TC有关。我认为这只是一个解决办法,当我们只是采取最后一个。
我想他们可以通过这种变通方法解决第一种情况(不编译),只需使用最后一个继承的实现。也要了解潜在冲突的来源,我们需要采用MyClass
的“观点”
让我们回顾一下你的例子,看看你为什么会得到这些结果(为了简化,我将使用2个特征,而不是3个)
第一个例子:通常的冲突
trait TA{def play()=println(“TA”)}
特征TB{def play()=println(“TB”)}
类MyClass使用TB{}扩展TA
这失败了,因为这两个特征定义了一个具有相同签名的play
方法,但不清楚为MyClass.play
选择哪个方法。有人可能会说“编译器可以选择TB.play,因为它是最后一个混合的trait”,但由于没有声明重写某些内容的意图,Scala拒绝了它。原则上它可以工作,但Scala团队选择迫使您更清楚地表达您的意图。
怎么做
明确意图1:在特质上超越
trait T{def play():Unit}
trait TA{override def play()=println(“TA”)}
trait TB{override def play()=println(“TB”)}
类MyClass使用TB{}扩展TA
第二个示例显示了表示覆盖意图的一种方式。由于TA.play
和TB.play
是用override
声明的,因此很明显,它们打算覆盖范围内的方法play():Unit
。这就是为什么编译器允许TB.play
在MyClass
中“替换”TA.play
明确意图2:在类中重写
class MyClass用TB扩展TA{
覆盖def播放():单位={
super.play()//super[TB].play()
超级[TA].play()
}
}
重写类中的方法是解决问题的另一种方法。这里我们确切地知道什么是MyClass.play
,因为它是显式定义的。从MyClass
的角度来看,没有歧义
编译器可以使用super[TA].play()
,因为调用的方法很清楚:在trait TA
中由def play()=println(“TA”)
定义的方法。因此super[TA].play()
将执行println(“TA”)
super.play()
没有那么明确,但是类组成的规则很清楚:super
在这个上下文中指的是最后一个mixin,也就是说,它与super[TB]
在最后一个类中显式重写并手工选择要调用的实现相同。这显然是合法的。这就是mixin的工作原理。@texasbruce但我只是在最后一个类中重写,TB
中的play
方法,TB
和TC
仍然冲突。换句话说,在mixin之后,方法play
在TA
、TB
和TC
中的语义是什么?它们是覆盖
或隐藏
或未定义
?令我惊讶的是,为什么我不需要在TB
和TC
中标记覆盖
?在TB
和TC
类MyClass
中方法play
的语义是什么?它们是不同的方法
或覆盖
或隐藏
或未定义
或冲突
?您说过“我想他们可以通过此解决方法解决第一种情况(未编译)”。我不这么认为,因为正如我在问题中所说的,Scala中没有冲突成员的默认行为。如果Scala像Java一样有默认的override
行为,那么您是对的