gpt4 book ai didi

scala - 可堆叠特征中的继承和代码重用

转载 作者:行者123 更新时间:2023-12-04 17:22:47 25 4
gpt4 key购买 nike

在这个简化的实验中,我希望能够快速构建一个具有可堆叠特征的类,该类可以报告用于构建它的特征。这让我想起了装饰器模式,但我更愿意在编译时而不是在运行时实现它。

带有冗余代码的工作示例

class TraitTest {
def report(d: Int) : Unit = {
println(s"At depth $d, we've reached the end of our recursion")
}
}

trait Moo extends TraitTest {
private def sound = "Moo"
override def report(d: Int) : Unit = {
println(s"At depth $d, I make the sound '$sound'")
super.report(d+1)
}
}
trait Quack extends TraitTest {
private def sound = "Quack"
override def report(d: Int) : Unit = {
println(s"At depth $d, I make the sound '$sound'")
super.report(d+1)
}
}

执行 (new TraitTest with Moo with Quack).report(0)然后会报告:
> At depth 0, I make the sound 'Quack'
At depth 1, I make the sound 'Moo'
At depth 2, we've reached the end of our recursion

不幸的是,那里有很多冗余代码让我眼花缭乱。我清理它的尝试导致我:

没有冗余代码的非工作示例
class TraitTest {
def report(d: Int) : Unit = {
println(s"At depth $d, we've reached the end of our recursion")
}
}

abstract trait Reporter extends TraitTest {
def sound : String
override def report(d: Int) : Unit = {
println(s"At depth $d, I make the sound '${sound}'")
super.report(d+1)
}
}

trait Moo extends Reporter {
override def sound = "Moo"
}
trait Quack extends Reporter{
override def sound = "Quack"
}

当我们再次执行 (new TraitTest with Moo with Quack).report(0) ,我们现在看到:
> At depth 0, I make the sound 'Quack'
At depth 1, we've reached the end of our recursion

问题一: “Moo”的台词去哪儿了?

我猜 Scala 只能看到 override def report(d: Int)一次,因此只将其放入继承链中一次。我捕获了稻草,但如果是这样的话,我该如何解决呢?

问题二:每个具体性状如何提供独特的 sound ?

解决第一个问题后,我会假设执行 (new TraitTest with Moo with Quack).report(0) 的结果由于 sound 的继承方式,看起来类似于以下内容会工作。
> At depth 0, I make the sound 'Quack'
At depth 1, I make the sound 'Quack'
At depth 2, we've reached the end of our recursion

我们怎样才能使每个特征都使用 sound在它的实现中指定?

最佳答案

一个特质最多可以被继承一次。它基本上只是一个扩展的java接口(interface)
scala 编译器的非抽象方法。

在构建具体类时,所有继承的特征都会线性化,因此您可以定义堆叠特征的顺序。如果您两次继承一个特征,则仅包含第一次出现的特征。所以在

class C1 extends A with B 
class C2 extends C1 with X with B

B trait 在线性化继承堆栈中的位置将在 A 之后但在 C1 和 X 之前。第二个 B 混合被忽略。

由于删除,即使使用类型参数之类的技巧也不起作用。所以这不起作用:
class X extends A with T[Int] with T[String]

(这可以在没有删除的平台上工作,例如 .NET)

个人经验的一些建议

我认为虽然堆叠特征有时是一个不错的功能,但如果你有一个带有堆叠特征的大型继承层次结构,它可能是维护的噩梦。功能取决于混入特征的顺序,因此只需更改特征顺序即可破坏您的程序。

此外,对不可变对象(immutable对象)的类层次结构使用继承几乎需要使用显式的自类型类型参数,这带来了另一个层次的复杂性。例如,请参阅 scala 集合中的 xxxLike 特征。

当特征不重叠时,它们当然非常有用且没有问题。但总的来说,对于 scala 和其他 OO 语言一样,优先组合优于继承的规则是正确的。 Scala 为您提供了强大的特征继承工具,但它也为您提供了可以说是更强大的组合工具(值类、隐式、类型类模式......)

帮助管理大型特征层次结构
  • 有一些工具可以强制执行某个命令。例如,如果 trait 中的方法未标记为 override,则不能将其混入已经实现该方法的类中。当然,如果您在 trait 中将方法标记为 final方法,则可以确保它始终处于“顶部”。无论如何,在特征中标记 final方法是一个非常好的主意。
  • 如果您决定使用复杂的特征层次结构,则需要一种检查特征顺序的方法。这以 scala 反射的形式存在。看到这个答案mixin order using reflection .

  • 示例如何使用 scala 反射获取特征顺序
    import scala.reflect.runtime.universe._
    class T extends TraitTest with Moo with Quack
    scala> typeOf[T].baseClasses
    res4: List[reflect.runtime.universe.Symbol] =
    List(class T, trait Quack, trait Moo, class TraitTest, class Object, class Any)

    不过,您需要在类路径中包含 scala-reflect.jar,它现在是一个单独的依赖项。我刚刚使用了一个 sbt 项目,添加了
    libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.10.2"

    构建.sbt 并启动 sbt console 。

    关于scala - 可堆叠特征中的继承和代码重用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17500342/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com