Scala特质混入中的方法调用顺序技术问询
First, let's fix up your incomplete BasicIntQueue class so we can run and test behavior properly:
import scala.collection.mutable.ArrayBuffer abstract class IntQueue { def get(): Int def put(x: Int) } trait Doubling extends IntQueue{ abstract override def put(x: Int) { println("In Doubling's put") super.put(2*x) } } trait Incrementing extends IntQueue { abstract override def put(x: Int) { println("In Incrementing's put") super.put(x + 1) } } class BasicIntQueue extends IntQueue { private val buf = new ArrayBuffer[Int] def get() = buf.remove(0) def put(x: Int) { println("In BasicIntQueue's put") buf += x } }
Now, the key to understanding method call order when mixing traits in Scala is linearization—Scala creates a linear inheritance chain from your base class and mixed-in traits, and super calls follow this chain in a specific order. Let's walk through two common scenarios:
Scenario 1: Mix Incrementing first, then Doubling
If you create an instance like this:
val queue = new BasicIntQueue with Incrementing with Doubling queue.put(10)
Scala's linearization order here is:Doubling → Incrementing → BasicIntQueue → IntQueue → AnyRef → Any
Here's what happens when you call put(10):
- We enter the last mixed-in trait first (
Doubling): it prints "In Doubling's put", then callssuper.put(2*10 = 20). - Next, we move to the previous trait in the chain (
Incrementing): it prints "In Incrementing's put", then callssuper.put(20 + 1 = 21). - Finally, we reach the base class (
BasicIntQueue): it prints "In BasicIntQueue's put" and adds 21 to the buffer.
The output will look like:
In Doubling's put In Incrementing's put In BasicIntQueue's put
Scenario 2: Mix Doubling first, then Incrementing
Swap the order of traits when creating the instance:
val queue = new BasicIntQueue with Doubling with Incrementing queue.put(10)
Now the linearization order flips to:Incrementing → Doubling → BasicIntQueue → IntQueue → AnyRef → Any
The call sequence changes accordingly:
- First, we enter
Incrementing(last mixed-in trait): prints "In Incrementing's put", callssuper.put(10 + 1 = 11). - Next, we move to
Doubling: prints "In Doubling's put", callssuper.put(2*11 = 22). - Finally,
BasicIntQueuehandles the put: prints "In BasicIntQueue's put" and adds 22 to the buffer.
Output here is:
In Incrementing's put In Doubling's put In BasicIntQueue's put
Core Rules to Remember
- Right-to-left linearization: When using
withto mix multiple traits, the last trait you list gets top priority in the linear chain—supercalls work their way leftward through the traits until they hit the base class. abstract overrideis mandatory: This modifier lets a trait override an abstract method from the base class while still callingsuper, since the concrete implementation will come from the class/trait it's mixed into.- No duplicate calls: Linearization ensures each class/trait is only included once in the chain, so you don't get redundant method executions.
内容的提问来源于stack exchange,提问作者Mandroid

