任何类只能`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`,则它调用其左侧特质的方法。
- Scala是什么?
- Scala特性
- 开发环境搭建
- 环境搭建
- windows下的环境搭建
- IntelliJ IDEA环境搭建
- Scala关键字
- Hello, World
- 数据类型
- 数据类型
- 数据类型层次结构
- 字面量
- Null类型
- Nothing类型
- Unit类型
- 变量与常量
- type定义类型别名
- 字符串插值器
- 条件控制
- 循环控制
- 数组
- 元组
- 集合
- 集合分类
- List常用操作
- Set常用操作
- Map常用操作
- 函数
- 函数声明与调用
- 函数与方法的区别
- 函数注意事项
- 匿名函数
- 可变参数
- 高阶函数
- 中置表达式
- 函数嵌套
- 函数科里化
- 隐式参数
- 隐式函数
- 闭包
- 类和对象
- Java与Scala的比较
- 有关类与对象概念
- 类
- 类的定义和调用
- 类的继承
- 抽象类
- 单例对象
- 伴生对象和伴生类
- 特质
- 定义特质
- 混入特质
- 抽象类与特质的选择
- 自身类型
- 依赖注入
- this别名
- 样例类
- 枚举类
- 泛型类
- 包与包对象
- 模式匹配
- 基本语法
- 匹配模式
- 偏函数
- 注解
- 运算符
- 正则表达式
- 隐式类
- 异常处理
- 高级类型
- 结构类型
- 复合类型