Step into Scala - 15 - 特质

本文深入探讨Scala中的特质概念,包括特质的定义、实现及继承方式,同时解析特质与抽象类的区别,帮助读者理解如何在实际编程中有效运用特质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

摘要

特质概念,定义特质,抽象方法,抽象属性,实现多个特质,继承特质,带有特质的对象,特质构造顺序,特质与抽象类

特质

概念

  • 特质是一些字段和行为的集合
  • 特质可以同时拥有抽象方法和具体方法
  • 一个类可以实现多个特质
  • 特质不能有带参数的构造函数。

定义一个特质

trait Logger {
  //字段
  val brand: String
  val minLength: Int

  //行为
  //抽象方法
  def log(msg: String)

  //带有具体实现
  def echo(msg: String) {
    println(msg)
  }
  //依赖抽象方法
  def info(msg: String) = log("INFO" + msg)
}
  • 特质使用关键字 trait 声明
  • 特质中可以有抽象方法和抽象属性,且复写时无需使用关键字 override
  • 特质中的方法可以互相依赖,即使是依赖抽象方法

实现特质

实现特质使用关键字 extends,之后如果需要实现多个特质时,则使用关键字 with

class ConsoleLogger extends Logger with Cloneable with Serializable {
  //实现抽象方法
  def log(msg: String): Unit = println(msg)

  //实现抽象属性
  val minLength: Int = 1
}

以上代码实际内部是将 Logger with Cloneable with Serializable 看作了一个整体类进行了继承。

继承特质

trait Logger {
  def show(msg: String) {}
  def log(msg: String)
}
trait OutputLogger extends Logger{
  override def show(msg: String): Unit = println(msg)
  //重写抽象方法
  abstract override def log(msg: String): Unit = {
    super.log(msg)
  }
}
  • 特质也可以继承特质
  • 如果需要在重写的抽象方法中调用父类特质的抽象方法,必须将方法本身声明为抽象的,即 abstract

带有特质的对象

可以为单个对象添加特质

trait Logger {
  def show(msg: String) {}
}
trait OutputLogger extends Logger{
  override def show(msg: String): Unit = println(msg)
}
class LoggerExample extends Logger {}

val logger1 = new LoggerExample
logger1.say("hello")

//为单个对象添加特质
val logger2 = new LoggerExample with OutputLogger
logger2.say("hello world") //hello world

特质的构造顺序

特质构造顺序遵循以下原则

  • 首先调用超类的构造器
  • 特质构造器在超类构造器之后,类构造器之前执行
  • 特质由左至右被构造
  • 每个特质中,父特质先被构造
  • 如果多个特质有同一个父特质,且父特质已被构造了,则不会被再次构造
  • 所有特质构造完毕,子类才被构造

有如下类

class SavingAccount extends Account with FileLogger with ShortLogger

则构造顺序为

  1. Account
  2. Logger
  3. FileLogger
  4. ShortLogger
  5. SavingAccount

在 ShortLogger 中调用 super 会执行 FileLogger 的方法

叠加在一起的特质

一般情况下,特质总是从最后一个开始向前执行

trait Logger {
  def show(msg: String) {}
}
trait TimeStampLogger extends Logger {
  override def show(msg: String): Unit = {
    super.show(new Date() + " " + msg)
  }
}
trait ShortLogger extends Logger {
  val maxLength = 15

  override def show(msg: String): Unit = {
    super.show(
      if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "..."
    )
  }
}
class LoggerExample extends Logger {}

val logger3 = new LoggerExample with OutputLogger with TimeStampLogger with ShortLogger
logger3.show("hello world logger3") //Mon Feb 16 11:46:06 CST 2015 hello world ...
val logger4 = new LoggerExample with OutputLogger with ShortLogger with TimeStampLogger
logger4.show("hello world logger4") //Mon Feb 16 1...

什么时候应该使用特质而不是抽象类?

如果你想定义一个类似接口的类型,你可能会在特质和抽象类之间难以取舍。这两种形式都可以让你定义一个类型的一些行为,并要求继承者定义一些其他行为。一些经验法则:

  • 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
  • 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。
  • 如果需要类从 Java类继承,使用抽象类。
  • 如果需要考虑效率问题,使用抽象类。Java的动态绑定机制决定了直接方法要快于接口方法。而特质最终是被编译成接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值