💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
任何类只能`extends`一个父类,但可以混入多个特质,混入用关键字`with`实现,相当于Java中用`implements`关键字实现接口一样。 ```scala // 1. 在声明类的时候混入特质 class A extedns B with TraitC with TraitD with ... // 2. 在new对象时混入特质 val A = new A with TraitC with TraitD with ... ``` <br/> **1. 混入特质** ```scala // 抽象类 abstract class Animal { val message:String = "abstract class Animal" } // 类 class Dog extends Animal { override val message: String = "class Dog" } // 特质 trait Mammal extends Animal { override val message: String = "trait Mammal" } // 特质 trait Pet extends Animal { override val message: String = "trait Pet" } // 混入特质 class Twoha extends Dog with Mammal with Pet object App { def main(args: Array[String]): Unit = { val twoHa = new Twoha println(twoHa.message) // trait Pet } } ``` * 当混入有多个同名的成员时,调用的是最右边的成员。如上面输出的`trait Pet`为最右边的`message`,当然如果子类重写了该成员,调用的就是子类的了。 **2. 混入特质的构造顺序** 构造顺序由左往右,如果前面已经有某个父类被构造一次,则后面不再重新构造,比如上面的Animal类虽然被多个子类继承,但从左到右只被构造一次。 具体构造顺序从左往右如下: ```scala 超类构造器 -> 父特质构造器 -> 子特质构造器 -> 子类构造器 所以 class Twoha extends Dog with Mammal with Pet 的构造顺序如下: Animal -> Dog -> Mammal -> Pet -> Twoha ``` <br/> **3. 特质对 `super`的动态调用** ```scala class Root { def hello() { println("Hello, Root!")} } class SubA extends Root { override def hello(){ super.hello() // 在类中super调用为静态调用 println("Hello, SubA!") } } trait SubB extends Root { override def hello(){ super.hello(); // 在特质中super调用是动态调用 println("Hello, SubB") } } ``` 在 SubA 中,`super` 的调用是静态绑定的,父类 Root 的 hello() 将被调用。而在 SubB 中,`super` 的调用是动态绑定的,即在定义特质 SubB 的时候,`super` 还不确定,直到特质被混入到具体类的时候才确定。 ```scala object HelloWorld{ def main(args: Array[String]): Unit = { val a = new Root with SubB // SubB被混入到Root中,则super为Root类, super.hello()为Root中的hello() a.hello() // Hello, Root! // Hello, SubB! val b = new SubA with SubB // SubB被混入到SubA中,则super为SubA类, super.hello()为SubA中的hello() // 调用是SubA中的hello() b.hello() // Hello, Root! // Hello, SubA! // Hello, SubB! } } ``` <br/> **4. 特质线性化** 线性化给了在多个特质中 `super` 被解析的顺序。 ```scala class Animal trait Furry extends Animal trait HasLegs extends Animal trait FourLegged extends HasLegs class Cat extends Animal with Furry with FourLegged ``` 类 Cat 的继承层级和线性化次序展示在下图。 继承次序使用传统的 UML 标注指明:白色箭头表明继承,箭头指向超类型。黑色箭头说明线性化次序,箭头指向 `super` 调用解决的方向。 ![](https://img.kancloud.cn/3f/22/3f22f49af22a2a85f5bb078e9d9cb805_1155x425.png) Cat 的线性化次序为:Cat >> FourLegged >> HasLegs >> Furry >> Animal `>>`意思是:串接并去掉重复项, 右侧胜出,如下 ```scala class Cat extends Animal with Furry with FourLegged lin(Cat) = Cat >> lin(FourLegged) >> lin(Furry) >> lin(Animal) = Cat >> (FourLegged >> HasLegs) >> (Furry >> Animal) >> (Animal) = Cat >> FourLegged >> HasLegs >> Furry >> Animal ``` <br/> **5. 特质调用链** 特质调用链就是子特质重写父特质的方法,然后在该方法中使用`super`关键字调用父特质的方法。 ```scala trait TraitA { def print = println("traitA") } trait TraitB extends TraitA { override def print: Unit = { super.print // 使用super调用父类的print函数 println("traitB") } } trait TraitC extends TraitB { override def print: Unit = { super.print println("traitC") } } class ClassD {} object App { def main(args: Array[String]): Unit = { val classD = new ClassD with TraitC with TraitB with TraitA classD.print } } ==========Output========== traitA traitB traitC ``` 可以看到 print 方法<mark>从右往左</mark>开始被调用,形成一个调用链。 总结:越靠近后面的特质越优先起作用,当调用带混入的类的方法时,最右侧特质的方法首先被调用。如果哪个方法调用了 `super`,则它调用其左侧特质的方法。