当前位置: 首页 > news >正文

六、Scala特质

在上一章中,我们学习了 Scala 的继承体系,但类只能直接继承自一个父类,这限制了代码的复用能力。为了解决这个问题,Scala 提供了一个非常强大的特性——特质 (Trait)。Trait类似于 Java 8+ 中的接口,但功能更加强大,它可以包含具体实现的方法和字段,是 Scala 中实现代码组合和行为注入的核心机制。

思维导图

在这里插入图片描述
在这里插入图片描述

一、特质入门

特质可复用代码片段,用于封装一组方法和字段。一个类可以混入 多个特质,从而获得这些特质定义的行为

基本语法:

定义特质: trait TraitName { ... }
类混入特质:
如果类没有父类,使用 extends 关键字混入第一个特质。
如果类已有父类,或需要混入多个特质,使用 with 关键字。

代码案例:

// 定义一个记录日志的特质
trait Logger {// 抽象方法,混入的类需要实现def log(message: String): Unit// 具体方法def info(message: String): Unit = log(s"INFO: $message")def warn(message: String): Unit = log(s"WARN: $message")
}// ConsoleLogger 类实现了 Logger 特质
class ConsoleLogger extends Logger {// 实现抽象方法override def log(message: String): Unit = println(message)
}// 一个类可以混入多个特质
trait TimestampLogger extends Logger {// 重写具体方法override def log(message: String): Unit = {println(s"${java.time.Instant.now()} $message")}
}// MyService 类继承自 Object (默认),并混入了 TimestampLogger
class MyService extends TimestampLogger {def performAction(): Unit = {info("Action started.")warn("Something might be wrong.")}
}val service = new MyService()
service.performAction()

二、Trait 的高级特性

1. 对象混入 Trait

Scala 允许创建对象实例时,动态地为其混入特质。这使得只有这个特定的实例拥有该特质的行为

代码案例:

class Person(val name: String)trait Singer {def sing(): Unit = println("La la la...")
}val person1 = new Person("Alice")
// person1.sing() // 编译错误,Person 类没有 sing 方法// 在创建 person2 实例时,动态混入 Singer 特质
val person2 = new Person("Bob") with Singerperson2.sing() // 正确,person2 实例拥有了 sing 方法

2. Trait 继承 Class

Trait 也可以继承自一个。这样做会施加一个约束:任何混入该 Trait 的类,必须是该 Trait 所继承的那个类子类

代码案例:

class Service {def serviceName: String = "Base Service"
}// MyServiceTrait 继承自 Service 类
trait MyServiceTrait extends Service {def extendedFeature(): Unit = println(s"${serviceName} has an extended feature.")
}// ValidService 继承自 Service, 所以可以混入 MyServiceTrait
class ValidService extends Service with MyServiceTrait// class InvalidService { ... }
// val invalid = new InvalidService with MyServiceTrait // 编译错误!
// 因为 InvalidService 不是 Service 的子类

三、Trait 的构造机制

当一个类继承了父类并混入了多个特质时,它们的构造器按照一个明确的线性化 顺序依次执行

构造顺序规则:

  1. 首先执行父类的构造器。
  2. 然后,从左到右依次执行每个 Trait 的构造代码。
  3. 最后执行子类的构造器。

代码案例:

class Base { println("Constructing Base") }
trait T1 extends Base { println("Constructing T1") }
trait T2 extends Base { println("Constructing T2") }
trait T3 extends Base { println("Constructing T3") }// Child 继承 T1 并混入 T2 和 T3
class Child extends T1 with T2 with T3 {println("Constructing Child")
}println("Creating a new Child instance:")
val c = new Child

输出结果:

Creating a new Child instance:
Constructing Base
Constructing T1
Constructing T2
Constructing T3
Constructing Child

解析:尽管所有特质都继承自 Base,但 Base 的构造器只会被执行一次。然后按照 with 关键字从左到右的顺序,依次执行 T1, T2, T3 的构造代码,最后才是 Child 自己的构造代码。

四、使用 Trait 实现设计模式

Trait 的灵活性使其成为实现多种设计模式的理想工具

1. 适配器模式

使用 Trait 可以优雅地将一个已存在的类接口转换成客户端期望的另一个接口。

代码案例:

// 目标接口
trait Target {def request(): Unit
}// 已存在的类 (被适配者)
class Adaptee {def specificRequest(): Unit = println("Adaptee's specific request.")
}// 适配器:继承 Adaptee,混入 Target Trait
class Adapter extends Adaptee with Target {override def request(): Unit = {// 将对 request() 的调用适配到对 specificRequest() 的调用this.specificRequest()}
}val adapter: Target = new Adapter()
adapter.request()

2. 模板方法模式

Trait 非常适合定义一个算法的骨架,而将一些步骤延迟到混入该 Trait 的子类中去实现。

代码案例:

trait DataProcessor {// 模板方法,定义了算法骨架,声明为 final 防止被重写final def process(): Unit = {readData()processData()writeData()}// 抽象方法,由子类实现protected def readData(): Unitprotected def processData(): Unitprotected def writeData(): Unit
}class FileDataProcessor extends DataProcessor {override protected def readData(): Unit = println("Reading data from a file.")override protected def processData(): Unit = println("Processing file data.")override protected def writeData(): Unit = println("Writing processed data to a file.")
}val fileProcessor = new FileDataProcessor()
fileProcessor.process()

3. 职责链模式

Trait 的线性化super 调用机制可以巧妙地实现职责链模式。

代码案例:

// 处理器基类
abstract class Handler {def handle(request: String): String
}// 具体的处理器,通过 Trait 实现
trait UppercaseHandler extends Handler {abstract override def handle(request: String): String = {super.handle(request.toUpperCase)}
}
trait ReverseHandler extends Handler {abstract override def handle(request: String): String = {super.handle(request.reverse)}
}// 链的终点
class FinalHandler extends Handler {override def handle(request: String): String = {s"Final result: $request"}
}// 通过混入 Trait 来构建职责链
val chain = new FinalHandler with UppercaseHandler with ReverseHandler
println(chain.handle("hello"))
// 输出: Final result: OLLEH

解析abstract override 是关键。当 chain.handle 被调用时,调用链ReverseHandler -> UppercaseHandler -> FinalHandler (从右到左)。super.handle 会将请求传递给线性化顺序中的前一个处理器。

五、综合案例

这个案例将展示如何使用 Trait 来为一个基类灵活地组合不同的功能模块,例如数据源格式化分发渠道。

代码实现:

// 基类,定义报告生成的骨架
class Report(val title: String) {def generate(): Unit = {println(s"--- Generating Report: $title ---")// 基础生成逻辑println("Core report generation logic finished.")}
}// 定义不同功能的 Trait
trait FromDatabase {def loadData(): Unit = println("Loading data from database...")
}
trait AsPDF {def format(): Unit = println("Formatting report as PDF...")
}
trait AsCSV {def format(): Unit = println("Formatting report as CSV...")
}
trait SendByEmail {def send(recipient: String): Unit = println(s"Sending report to $recipient via Email...")
}
trait UploadToFTP {def send(location: String): Unit = println(s"Uploading report to FTP server at $location...")
}// 通过混入 Trait 定义不同类型的报告生成器
// 案例1:一个从数据库获取数据,生成PDF,并通过邮件发送的报告
class MonthlySalesReport(title: String) extends Report(title) with FromDatabase with AsPDF with SendByEmail {// 可以重写或扩展方法override def generate(): Unit = {loadData()format()super.generate() // 调用基类的核心逻辑send("management@example.com")println("--- Report generation complete ---")}
}// 案例2:一个生成CSV并通过FTP上传的报告
class DailyInventoryReport(title: String) extends Report(title) with AsCSV with UploadToFTP {override def generate(): Unit = {format()super.generate()send("ftp://inventory.server/daily/")println("--- Report generation complete ---")}
}// 动态为对象添加功能
val adHocReport = new Report("Ad-hoc Analysis")
// 假设这个临时报告需要PDF格式
val pdfAdHocReport = adHocReport with AsPDF// 使用
println("--- Running Monthly Sales Report ---")
val salesReport = new MonthlySalesReport("October Sales")
salesReport.generate()println("\n--- Running Daily Inventory Report ---")
val inventoryReport = new DailyInventoryReport("Inventory Status")
inventoryReport.generate()println("\n--- Running Ad-hoc Report ---")
pdfAdHocReport.format()
pdfAdHocReport.generate()

练习题

题目一:基本 Trait 混入
定义一个 HasEngine 特质,包含一个具体方法 startEngine(),打印 “Engine started.”。然后定义一个 Car 类,混入这个特质,并创建一个实例调用 startEngine 方法。

题目二:Trait 与抽象成员
定义一个 CanFly 特质,包含一个抽象字段 maxAltitude: Int。然后创建一个 Drone 类,混入该特质,并将 maxAltitude 实现为 500。创建 Drone 实例并打印其 maxAltitude

题目三:多个 Trait 混入
定义两个特质:Floats (有 float() 方法,打印 “Floating on water.”) 和 Flies (有 fly() 方法,打印 “Flying in the air.”)。创建一个 Seaplane 类,同时混入这两个特质,并创建实例分别调用 floatfly 方法。

题目四:对象混入 Trait
创建一个 Robot 类。然后,创建一个 Robot 的实例 cookingRobot,并在创建时动态地为其混入一个 Cook 特质 (该特质有一个 cookDinner() 方法,打印 “Cooking dinner.”)。最后调用 cookingRobotcookDinner 方法。

题目五:Trait 继承 Class
创建一个 Appliance 类。创建一个 CanConnectToWifi 特质,继承自 Appliance。然后创建一个 SmartFridge 类,继承自 Appliance 并混入 CanConnectToWifi 特质。

题目六:Trait 构造顺序
预测以下代码的输出结果,并写出最终的输出。

trait T1 { println("Init T1") }
class C1 { println("Init C1") }
class C2 extends C1 with T1 { println("Init C2") }
val instance = new C2()

题目七:Trait 构造顺序 (多特质)
预测以下代码的输出结果,并写出最终的输出。

trait T_A { println("A") }
trait T_B { println("B") }
class C_Base { println("Base") }
class C_Child extends C_Base with T_A with T_B { println("Child") }
val child_instance = new C_Child()

题目八:适配器模式
有一个 LegacyPrinter 类,它有一个 printDocument(content: String, copies: Int) 方法。你需要一个符合 SimplePrinter 特质 (有 print(content: String) 方法) 的对象。请使用适配器模式创建一个 PrinterAdapter 类,使其调用 print 方法时,默认打印1份。

题目-九:模板方法模式
创建一个 Builder 特质,它有一个 final 的模板方法 build(),该方法依次调用三个抽象方法:layFoundation(), buildWalls(), addRoof()。然后创建一个 HouseBuilder 类来实现这三个步骤,每个步骤打印相应的信息。

题目十:职责链模式
使用 superabstract override,创建两个处理器 Trait:HeaderHandler (在字符串前加 "[HEADER] “) 和 FooterHandler (在字符串后加 " [FOOTER]”)。将它们混入一个 ContentHandler (它只返回原始内容),并构造一个调用链处理字符串 “My Data”。

题目十一:Trait 中的字段初始化
定义一个 HasID 特质,它有一个具体字段 val id: String = java.util.UUID.randomUUID().toString。创建一个 User 类混入此特质,并创建两个 User 实例,打印它们的 id,观察ID是否相同。

题目十二:Trait 与 self-type
创建一个 Notifier 特质,它需要日志功能,但本身不实现。使用 self-type 注解,强制要求任何混入 Notifier 特质的类,必须也混入一个 Logger 特质 (该特质有 log(msg: String) 方法)。然后创建一个合法的 EmailNotifier 类。

题目十三:Trait 解决菱形继承问题
定义一个基特质 Worker,以及两个继承自 Worker 的特质 CanCodeCanManage,它们都重写了 work() 方法。创建一个 TeamLead 类,同时混入 CanCodeCanManage,并观察 super.work() 调用的是哪个实现 (根据线性化规则)。

题目十四:abstract override 的应用
创建一个 Logger 特质,有一个 log(msg: String) 方法。再创建一个 TimestampLogger 特质,使用 abstract override 来为 log 方法添加时间戳前缀。

题目十五:综合案例
定义一个 Character 基类。定义 FighterMage 两个 Trait,分别有 fight()castSpell() 方法。创建一个 Spellsword 类,它继承自 Character 并同时混入 FighterMage

答案与解析

答案一:

trait HasEngine {def startEngine(): Unit = println("Engine started.")
}
class Car extends HasEngineval myCar = new Car()
myCar.startEngine()

答案二:

trait CanFly {val maxAltitude: Int
}
class Drone extends CanFly {override val maxAltitude: Int = 500
}
val myDrone = new Drone()
println(s"Drone max altitude: ${myDrone.maxAltitude}")

答案三:

trait Floats { def float(): Unit = println("Floating on water.") }
trait Flies { def fly(): Unit = println("Flying in the air.") }
class Seaplane extends Floats with Fliesval seaplane = new Seaplane()
seaplane.float()
seaplane.fly()

答案四:

class Robot
trait Cook {def cookDinner(): Unit = println("Cooking dinner.")
}val cookingRobot = new Robot with Cook
cookingRobot.cookDinner()

解析: with 关键字可以在创建实例时动态地为对象添加新的特质和行为。

答案五:

class Appliance
trait CanConnectToWifi extends Appliance
class SmartFridge extends Appliance with CanConnectToWifi
// 如果 SmartFridge 不继承 Appliance,则混入 CanConnectToWifi 会编译失败

解析: 当一个 Trait 继承自某个类时,它就为所有混入它的类设定了一个“必须是该父类的子类”的约束。

答案六:
输出:

Init C1
Init T1
Init C2

答案七:
输出:

Base
A
B
Child

答案八:

class LegacyPrinter {def printDocument(content: String, copies: Int): Unit = {for (i <- 1 to copies) println(content)}
}
trait SimplePrinter {def print(content: String): Unit
}
class PrinterAdapter extends LegacyPrinter with SimplePrinter {override def print(content: String): Unit = {this.printDocument(content, 1)}
}

答案九:

trait Builder {final def build(): Unit = {layFoundation()buildWalls()addRoof()}protected def layFoundation(): Unitprotected def buildWalls(): Unitprotected def addRoof(): Unit
}
class HouseBuilder extends Builder {override protected def layFoundation(): Unit = println("Laying the house foundation.")override protected def buildWalls(): Unit = println("Building the house walls.")override protected def addRoof(): Unit = println("Adding the house roof.")
}
val hb = new HouseBuilder()
hb.build()

答案十:

abstract class BaseHandler { def handle(request: String): String }
trait HeaderHandler extends BaseHandler {abstract override def handle(request: String): String = super.handle(s"[HEADER] $request")
}
trait FooterHandler extends BaseHandler {abstract override def handle(request: String): String = super.handle(s"$request [FOOTER]")
}
class ContentHandler extends BaseHandler {override def handle(request: String): String = request
}val chain = new ContentHandler with HeaderHandler with FooterHandler
println(chain.handle("My Data")) // 输出: [HEADER] My Data [FOOT-ER]

答案十一:

trait HasID {val id: String = java.util.UUID.randomUUID().toString
}
class User extends HasIDval user1 = new User()
val user2 = new User()
println(user1.id)
println(user2.id)
// 两个 id 将会不同

答案十二:

trait Logger { def log(msg: String): Unit }
trait Notifier {this: Logger => // Self-type: I must also be a Loggerdef notify(message: String): Unit = {log(s"NOTIFICATION: $message")}
}
class EmailNotifier extends Logger with Notifier {override def log(msg: String): Unit = println(s"LOG: $msg")
}

答案十三:

trait Worker { def work(): Unit = println("Working...") }
trait CanCode extends Worker {override def work(): Unit = {super.work() // 调用 Worker 的 workprintln("Coding...")}
}
trait CanManage extends Worker {override def work(): Unit = {super.work() // 调用 Worker 的 workprintln("Managing...")}
}
// 混入顺序决定 super 调用链, 从右到左: CanManage -> CanCode -> Worker
class TeamLead extends Worker with CanCode with CanManage
val lead = new TeamLead()
lead.work()
// 输出:
// Working...
// Coding...
// Managing...

答案十四:

trait Logger {def log(msg: String): Unit = println(msg)
}
trait TimestampLogger extends Logger {abstract override def log(msg: String): Unit = {super.log(s"${java.time.Instant.now()}: $msg")}
}
class MyLogger extends Logger with TimestampLogger
val logger = new MyLogger()
logger.log("System startup.")

答案十五:

class Character
trait Fighter {def fight(): Unit = println("Swinging a sword!")
}
trait Mage {def castSpell(): Unit = println("Casting a fireball!")
}
class Spellsword extends Character with Fighter with Mage
val spellsword = new Spellsword()
spellsword.fight()
spellsword.castSpell()

文章转载自:

http://6RQjQTdw.tkchg.cn
http://vhPYviv8.tkchg.cn
http://yPodG0jc.tkchg.cn
http://OglGZSXY.tkchg.cn
http://tz9CULb5.tkchg.cn
http://II9I533f.tkchg.cn
http://gxAhTgjC.tkchg.cn
http://qyaIQ8Mz.tkchg.cn
http://lSlzCEam.tkchg.cn
http://ffJdjDoh.tkchg.cn
http://CKBwn5al.tkchg.cn
http://BgirdJYh.tkchg.cn
http://HkMjQoAj.tkchg.cn
http://F6yz6H93.tkchg.cn
http://hfMVtfYW.tkchg.cn
http://ciwfwbH9.tkchg.cn
http://YTO9HQ5k.tkchg.cn
http://ZyGJwnAG.tkchg.cn
http://HGhuYEMM.tkchg.cn
http://enWsVstv.tkchg.cn
http://lN0XgFvV.tkchg.cn
http://rO5FPHtY.tkchg.cn
http://UBt4TH9Z.tkchg.cn
http://JXNQkxqe.tkchg.cn
http://MXPo5D3k.tkchg.cn
http://qxGFDahh.tkchg.cn
http://JwPz3bsF.tkchg.cn
http://PvDzYwRv.tkchg.cn
http://u1FNCWUP.tkchg.cn
http://vOmUL57W.tkchg.cn
http://www.dtcms.com/a/386907.html

相关文章:

  • 在LazyVim中配置Rust开发环境
  • Navicat x 金仓 KingbaseES 快速入门指南
  • 数据结构:完全二叉树
  • 将容器的日志记录到 Linux 日志系统
  • css中的伪类选择器---------nth-child()
  • 深度学习“调参”黑话手册:学习率、Batch Size、Epoch都是啥?
  • Vue: 组件 Props
  • spring通过Spring Integration实现tcp通信
  • 改革企业治理架构,构建国有企业全面预算管理体系
  • 网络概述学习
  • VRRP 实验
  • confulence平台
  • 非许可型区块链
  • 如何使用词嵌入模型
  • 从一个想法到上线:Madechango项目架构设计全解析
  • pytest入门
  • 设计模式第二章(装饰器模式)
  • ​​解决大模型幻觉全攻略:理论、技术与落地实践​
  • qt QCandlestickSeries详解
  • 量化研究--高频日内网格T0策略研究
  • [Dify] 自动摘要与精炼:构建内容浓缩型工作流的实践指南
  • Windows安装mamba最佳实践(WSL ubuntu丝滑版)
  • 黑马头条_SpringCloud项目阶段一:环境搭建(Mac版本)
  • Java 设计模式全景解析
  • 【Python】OS模块操作目录
  • 深度学习基本模块:LSTM 长短期记忆网络
  • 初始化Vue3 项目
  • 耕地质量评价
  • MeloTTS安装实践
  • 国产化芯片ZCC3790--同步升降压控制器的全新选择, 替代LT3790