Scala特质线性化机制与super调用行为问题咨询
这个现象是Scala的两个核心语言规则共同导致的,和Parent特质自身的继承层级没有直接关系:
- 特质的
super调用是动态绑定的:特质里写的super.xxx不是硬编码调用它自身声明继承的父类实现,而是会根据最终混入这个特质的实例的完整线性化链,调用链中排在当前特质之后的第一个实现。 - 混入顺序决定线性化优先级:特质声明时
with关键字越靠右的特质,在最终实例的super调用链里离当前子类越近,会排在更靠近子类的位置,插在靠左的特质和它原本的父类之间。 - 只要某一层的方法实现没有调用
super.nameIt,整个调用链就会直接终止,不会继续往后追溯。
先明确TheSmiths的super调用顺序
你定义TheSmiths的混入顺序是extends FabricEngineer with TextileEngineer with ClothMaker with Parent,按照越靠右的混入特质优先级越高的规则,从TheSmiths实例出发,super调用的完整线性化顺序为:TheSmiths → Parent → ClothMaker → Trader → TextileEngineer → FabricEngineer → Engineer → Person → Human
这里要注意:如果单独实例化Parent特质,它的下一级super确实是Person,但在TheSmiths的线性化链里,ClothMaker和Trader被插到了Parent和Person之间,Parent里写的super调用目标会随之改变。
走一遍override:3的调用流程就能完全理解
当你使用无类型限定的super.nameIt时,会从当前类TheSmiths的下一级(也就是链中第一个节点Parent)开始执行调用:
- 进入Parent的
nameIt实现,打印Parent,随后执行super.nameIt——这时候Parent的super不是它自身声明继承的Person,而是链里排在它后面的ClothMaker。 - 进入ClothMaker的
nameIt实现,打印ClothMaker,随后执行super.nameIt,调用链里排在它后面的Trader。 - 进入Trader的
nameIt实现,打印Trader——注意Trader的nameIt方法根本没有写super.nameIt调用,方法执行完直接结束,整个调用链在这里直接终止。 - 链路到Trader就断开了,根本走不到后面的TextileEngineer、FabricEngineer、Engineer、Person、Human节点,自然不会追溯到Human。
为什么指定类型的super调用能走到Human?
写super[TextileEngineer].nameIt或者super[FabricEngineer].nameIt时,会强制跳过链里排在指定特质前面的所有节点,直接从指定特质的位置开始走链:
- 从TextileEngineer开始走的话,后面的节点是FabricEngineer、Engineer、Person、Human,这几个节点的
nameIt实现都写了super调用,一直走到Human(Human的nameIt也没有写super调用)才终止,所以能输出到Human。 - 从FabricEngineer开始走的话,后面是Engineer、Person、Human,同理也能走到Human。
常见踩坑提示:不要用Java单继承的思维理解Scala的特质super调用,特质的super是为可堆叠改动设计的,混入顺序会直接改变调用链路,只要中间某层不调用super,后面的所有实现都不会被执行。
内容的提问来源于stack exchange,提问作者theutonium.18

