Scala 可堆叠特征

Scala stackable traits

根据我的理解,下面的代码是表达式的线性化

新E配D配C配B

是 E -> C -> B -> D。那么代码

中的表达式 d.foo() 不应该

下面评估为 ECBD 而不是 CBDE。我错过了什么?

    trait A {
      def foo(): String = ""
    }

    trait B extends A {
      abstract override def foo() = "B" + super.foo()
    }

    trait C extends B {
      abstract override def foo() = "C" + super.foo()
    }

    trait D extends A {
      abstract override def foo() = "D" + super.foo()
    }

    class E extends A{
      override def foo() = "E"
    }

    var d = new E with D with C with B;
    d.foo() //prints CBDE

我注意到如果我有一个像下面这样的 class F

class F extends A with D with C with B{
      override def foo() = "F" + super.foo()
}

并做

new F().foo

它打印 "FCBD"

这对我来说似乎有点不一致,因为class F 的混合方式与表达式相同,但打印顺序不同

第一种情况new E with D with C with B完美解释。它的线性化是EDBC,所以当你调用d.foo()时,它

  • 第一次调用 C#foo()
  • 然后 B#foo()
  • 然后 D#foo()
  • 最后 E#foo()

如果你让E成为一个特质,最后将其混合:val d = new D with C with B with E,那么d.foo()将return只是"E",因为特质E 是线性化中的 "last" 并且只是覆盖 foo.

F的情况不一样,因为你把foo定义为"F" + super.foo(),而super这里的A with D with C with B线性化为ADBC,所以 new F().foo() - 首先打印 "F", - 然后是 super.foo()"CBD"

顺便说一下,尝试将 A#foo() 更改为 return "A",然后你会看到在 E 中你覆盖了 A 的 foo 所以 "A"没有出现在结果中,在F中是"FCBDA".

所以让我们添加一些显示实例化顺序的行

  trait A {
    print("A")
    def foo(): String = "A"
  }

  trait B extends A {
    print("B")
    abstract override def foo() = "B" + super.foo()
  }

  trait C extends B {
    print("C")
    abstract override def foo() = "C" + super.foo()
  }

  trait D extends A {
    print("D")
    abstract override def foo() = "D" + super.foo()
  }

  class E extends A {
    print("E")
    override def foo() = "E" + super.foo()
  }

  var e = new E with D with C with B
  println()
  println(s"e >> ${e.foo()}")

印刷: AEDBC e >> CBDEA

但是 F 呢?

  class F extends A with D with C with B {
    print("F")
    override def foo() = "F" + super.foo()
  }
  val f = new F()
  println()
  println(s"f >> ${f.foo()}")

印刷: ADBCF f >> FCBDA

因为你可以看到两种情况下的线性化不同! 当我们用一系列特征实例化 class 时,它与我们创建一个单独的 class 继承这些特征时不同。

所以根据线性化,调用顺序 foo 也不同。 当我们将 super.foo() 添加到 E

时就有点清楚了