一、特质基础:是什么与核心作用
1. 特质的本质
特质(关键字 trait)是 Scala 中用于封装方法、字段和抽象成员的结构,核心作用有两个:
- 定义接口规范:包含抽象方法 / 字段,要求实现类必须实现;
- 提供代码复用:包含具体方法 / 字段,实现类可直接继承使用;
- 支持多继承:一个类可以同时继承多个特质(用
with连接),解决了单继承语言的灵活性限制。
二、核心细节:特质构造器执行顺序
特质的构造器执行顺序是新手最容易混淆的点,错误理解可能导致字段初始化异常。我们通过第一个案例拆解规则。
1. 案例:特质与类的继承关系
以下代码定义了多个特质和类,观察实例化子类时的构造顺序:
代码如下
package level02
/* 特质
* trait: 实现多继承
* 构造器的执行顺序哦
* 先父 后子
* 如果是多继承 有多个trait 按书写顺序从左到右
*/
object class16 {
trait AA {
println("A 特质构造器 ")
}
trait A extends AA{
println("A 构造器 ")
}
trait B {
println("B 特质构造器")
}
trait CC {
println("CC 特质构造器")
}
class C extends CC {
println("C 构造器")
}
class Child() extends C with B with A {
println("child...")
}//继承三个特质A,B,C
def main(args: Array[String]): Unit = {
val child = new Child()
}
}
2. 解决方案:早期初始化块
Scala 提供了 “早期初始化块”(Early Initializer),允许在子类继承特质之前,先初始化特质依赖的抽象字段,从而解决初始化顺序问题。
正确代码示例
package level02
import java.io.FileWriter
object class13 {
trait FileLogger {
val filename: String
val writer = new FileWriter(filename) // 此时filename已被早期初始化赋值
def writeLog(msg: String): Unit = {
writer.write(msg)
}
// 注意:writer.close() 不能放在这里!会导致写入前流已关闭
}
// 早期初始化块:{} 中的代码在特质初始化前执行
class MyWriter extends {
override val filename: String = "test.log"
} with FileLogger {
// 可选:在子类析构时关闭流(或使用try-with-resources)
def close(): Unit = writer.close()
}
def main(args: Array[String]): Unit = {
val log = new MyWriter()
log.writeLog("测试内容")
log.close() // 手动关闭流,确保内容写入文件
}
}
3. Trait 多继承与 Class 继承演示
package level02
import java.io.FileWriter
// trait 与 class 的区别
// 1. class 特点:单继承,抽象类,内部类,不支持多继承。
// 2. trait 特点:可以扩展多个特质(不能扩展普通类)。 extends with
// 共同点
// 1. 都可以有:抽象属性,抽象方法,具体方法,抽象方法
// 2. 都使用extends来继承
object class18 {
trait A{}
trait B{}
class AB extends A with B{
}
def main(args:Array[String]):Unit = {
}
}