千家信息网

大数据Scala系列之特质

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,大数据Scala系列之特质,特质的定义除了使用关键字trait之外,与类定义无异。特质用来在类之间进行接口或者属性的共享。类和对象都可以继承特质,特质不能被实例化,因此也没有参数。一旦特质被定义了,就
千家信息网最后更新 2026年11月03日大数据Scala系列之特质

  大数据Scala系列之特质,特质的定义除了使用关键字trait之外,与类定义无异。

  特质用来在类之间进行接口或者属性的共享。类和对象都可以继承特质,特质不能被实例化,因此也没有参数。

  一旦特质被定义了,就可以使用extends或者with在类中混入特质。

1 作为接口使用的特质
特质的定义:

trait Logger{
//这是一个抽象方法,特质中未被实现的方法默认是抽象的,不需要abstract关键字修饰
def log(msg:String)
}

子类对特质的实现:

class ConsoleLogger extends Logger{
//重写抽象方法,不需要override
def log(msg:String){println(msg)}
}

2 带有具体实现的特质
trait ConsoleLogger{
//注意与Java中接口的不同
def log(msg:String){println(msg)}
}

特质的使用

class SavingAccount extends Account with ConsoleLogger{
def withdraw(amount:Double){
if(amount >balance) log("Insufficent funds")
else balance -= amount
}
}

3 带有特质的对象
scala自带有Logged特质,但是没有实现

trait Logged{
def log(msg:String){}
}

如果在类定义中使用了该特质

//该类中,其中的日志信息不会被记录
class SavingAccount extends Account with Logged{
def withdraw(amount:Double){
if(amount >balance) log("Insufficent funds")
else balance -= amount
}
}

标准的ConsoleLogger扩展自Logger

class ConsoleLogger extends Logger{
//重写抽象方法,不需要override
def log(msg:String){println(msg)}
}

可以在创建对象的时候,加入该特质:

val acct1=new SavingAccount with ConsoleLogger

这样,创建同一类对象,却可以加入不同的特质

val acct2=new SavingAccount with FileLogger

4 多个叠加的特质
可以为类或者对象添加多个互相调用的特质,特质的执行顺序,取决于特质被添加的顺序

trait Logged{
def log(msg:String)
}
trait ConsoleLogger extends Logged{
//重写抽象方法,不需要override
def log(msg: String) ={println(msg)}
}
//给log加上时间戳
trait TimestampLogger extends ConsoleLogger {
override def log(msg: String) {
super.log(s"${java.time.Instant.now()} $msg")
}
}
//截断过于冗长的日志信息
trait ShortLogger extends ConsoleLogger{
val maxLength = 15
override def log(msg: String) {
super.log(
if(msg.length <=maxLength)msg
else
s"${msg.substring(0,maxLength-3)}...")
}
}
//定义超类
class Account {
protected var balance:Double = 0
}
class SavingAccount extends Account with ConsoleLogger{
def withdraw(amount:Double){
if(amount >balance) log("Insufficent funds")
else balance = balance - amount
}
}

object test{
def main(args: Array[String]): Unit = {
val acct1 = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger
val acct2 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger
acct1.withdraw(100.0)
acct2.withdraw(100.0)

}
}

//res:
//ShortLogger的log方法先被执行,然后它的super.log调用的是TimestampLogger 的log方法,最后调用ConsoleLogger 的方法将信息打印出来
2018-06-15T16:50:28.448Z Insufficent ...
//先是TimestampLogger 的log方法被执行,然后它的super.log调用的是ShortLogger的log方法,最后调用ConsoleLogger 的方法将信息打印出来
2018-06-15T1...

5 使用特质统一编程
import scala.collection.mutable.ArrayBuffer

trait Pet {
val name: String
}

class Cat(val name: String) extends Pet
class Dog(val name: String) extends Pet

val dog = new Dog("Harry")
val cat = new Cat("Sally")

val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name)) // Prints Harry Sally

Mixins用于进行类组合的特质:

abstract class A {
val message: String
}
class B extends A {
val message = "I'm an instance of class B"
}
//此处的特质C即为mixin
trait C extends A {
def loudMessage = message.toUpperCase()
}
class D extends B with C

val d = new D
println(d.message) // I'm an instance of class B
println(d.loudMessage) // I'M AN INSTANCE OF CLASS B

6 当做富接口使用的特质
//注意抽象方法和具体方法的结合
trait Logger { def log(msg: String)
def info(msg: String) { log("INFO: " + msg) }
def warn(msg: String) { log("WARN: " + msg) }
def severe(msg: String) {log("SEVERE: " + msg)}
}
class Account {
protected var balance:Double = 0
}
class SavingsAccount extends Account with Logger {
def withdraw(amount: Double) {
if (amount > balance) severe("Insufficient funds") else "you can do this" }
override def log(msg: String) { println(msg) }
}

object test{
def main(args: Array[String]): Unit = {
val acc = new SavingsAccount
acc.withdraw(100)
}
}
//result
SEVERE: Insufficient funds

7特质中的具体字段和抽象字段
特质中的字段有初始值则就是具体的,否则是抽象的。

trait ShortLogger extends Logged {
val maxLength = 15 // 具体字段
}

那么继承该特质的子类是如何获得这个字段的呢。Scala是直接将该字段放入到继承该特制的子类中,而不是被继承。例如:

class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
var interest = 0.0
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else ...
}
}

特质中的抽象字段在具体的子类中必须被重写:

trait ShortLogger extends Logged {
val maxLength: Int//抽象字段
override def log(msg: String) {
super.log( if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...")
}
}

class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
val maxLength = 20 // 不需要写override
}

8 特质构造顺序
特质也是有构造器的,由字段的初始化和其他特质体中的语句构成:

trait FileLogger extends Logger {
val out = new PrintWriter("app.log") // 构造器的一部分
out.println("# " + new Date().toString) // 也是构造器的一部分

def log(msg: String) { out.println(msg); out.flush() }
}

这些语句在任何混入了该特质的对象在构造时都会被执行。 构造器的顺序:

首先调用超类的构造器
特质构造器在超类构造器之后、类构造器之前执行
特质由左到右被构造
每个特质中,父特质先被构造
如果多个特质共有一个父特质,那么那个父特质已经被构造,则不会被再次构造
所有特质构造完毕后,子类被构造。 例如:
class SavingsAccount extends Account with FileLogger with ShortLogger

构造器执行顺序:

1Account (超类)

2 Logger (第一个特质的父特质)

3 FileLogger

4 ShortLogger

5 SavingsAccount

9 初始化特质中的字段
特质不能有构造器参数,每个特质都有一个无参构造器。这也是特质和类的差别。 例如: 我们要在构造的时候指定log的输出文件:

trait FileLogger extends Logger {
val filename: String // 构造器一部分
val out = new PrintWriter(filename) // 构造器的一部分
def log(msg: String) { out.println(msg); out.flush() }
}

val acct = new SavingsAccount extends Account with FileLogger("myapp.log") //error,特质没有带参数的构造器

// 你也许会想到和前面重写maxLength一样,在这里重写filename:
val acct = new SavingsAccount with FileLogger {
val filename = "myapp.log" // 这样是行不通的
}

FileLogger的构造器先于子类构造器执行。这里的子类其实是一个扩展自SavingsAccount 并混入了FileLogger特质的匿名类。而filename的初始化发生在这个匿名类中,而FileLogger的构造器会先执行,因此new PrintWriter(filename)语句会抛出一个异常。 解决方法是要么使用提前定义或者使用懒值:

val acct = new {
val filename = "myapp.log"
} with SavingsAccount with FileLogger

// 对于类同样:
class SavingsAccount extends {
val filename = "myapp.log"
} with Account with FileLogger {
... // SavingsAccount 的实现
}

// 或使用lazy
trait FileLogger extends Logger {
val filename: String // 构造器一部分
lazy val out = new PrintWriter(filename) // 构造器的一部分
def log(msg: String) { out.println(msg); out.flush() }
}

10 扩展类的特质
特质也可以扩展类,这个类将会自动成为所有混入该特质的超类

trait LoggedException extends Exception with Logged {
def log() { log(getMessage()) }
}

log方法调用了从Exception超类继承下来的getMessage 方法。那么混入该特质的类:

class UnhappyException extends LoggedException {
override def getMessage() = "arggh!"
}

特质 构造器 方法 字段 子类 对象 顺序 信息 接口 参数 多个 语句 不同 关键 关键字 日志 时候 数据 冗长 之间 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 上海挚品互联网科技有限公司 科技与互联网 互联网扶贫科技 互联网最新的科技有限公司 互联网科技小贷 互联网科技革命 安徽鑫晟辉互联网科技 九亚互联网科技 美国互联网前沿科技 佩信高科技互联网 卫星互联网科技股票 云创客互联网科技公司 现代科技和互联网有关系吗 常德市电业局互联网科技部 互联网金融科技公司干啥的 阿大互联网科技有限公司 中国科技创新与世界互联网大会 互联网公司高科技产品 宇信科技在杭州互联网排第几 互联网科技包装股份有限公司上市 深圳恒合互联网络科技有限公司 嘟嘟瓜互联网科技 火传媒上海互联网科技 剑昆互联网络科技有限公司临桂 中国航天科技大学国际工业互联网 南博望互联网科技有限公司 云鲸互联网络科技有限公司 保山互联网科技靠谱吗 广州三三互联网科技有限公司 小牛互联网科技有限公司地址
0