Scala 中的 特质(trait)

特质(trait)

scala中没有Java中的接口(interface),替代的概念是——特质

概念

  • 特质是scala中代码复用的基础单元
  • 它可以将方法和字段定义封装起来,然后添加到类中
  • 与类继承不一样的是,类继承要求每个类都只能继承一个超类,而一个类可以添加任意数量的特质。
  • 特质的定义和抽象类的定义很像,但它是使用trait关键字

语法

trait 名称 {
    // 抽象字段
    // 抽象方法
}
继承特质
  • 使用extends来继承trait(scala不论是类还是特质,都是使用extends关键字)
  • 如果要继承多个trait,则使用with关键字
class 类 extends 特质1 with 特质2 {
    // 字段实现
    // 方法实现
}
继承单个trait 代码示例
object Test01 {

  //定义一个  日志   特质
  trait Logger {
    //抽象方法  接收控制带
    def log(message:String)
  }

  //定义一个控制台类 继承日志类 输出日志信息
  class ConsoleLogger extends Logger {
    override def log(message: String): Unit = println("控制台日志:\n" + message)
  }

  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLogger
    logger.log("这是一条日志")
  }
}
继承多个特质 代码示例
object Test02 {
  //创建一个 消息发送  特质 添加 发送 方法
  trait MessageSender {
    //定义一个发送方法
    def send(msg:String)
  }

  //创建一个 消息接收  特质  添加 接收 方法
  trait MessageReceive {
    def receive() : String
  }

  //创建 消息工作 类 实现 接收和发送两个特质
  class MessageWorker extends MessageSender with MessageReceive {
    //继承发送方法
    override def send(msg: String): Unit = println(s"发送消息 : ${msg}")
    //继承接收方法
    override def receive(): String = "你好!我叫张三"
  }

  def main(args: Array[String]): Unit = {
    val worker = new MessageWorker
    //发送信息 hello
    worker.send("hello")
    //接收信息
    println(worker.receive())
  }
}

定义具体的方法

和类一样,trait中还可以定义具体的方法

代码示例
object Test03 {
  trait LoggerDetail {
    //在trait 中定义具体方法
    def log(msg:String): Unit = println(s"msg = ${msg}")
  }

  class UserService extends LoggerDetail {
    def add(): Unit = {
      //调用log 方法
      log("添加用户")
    }
  }


    def main(args: Array[String]): Unit = {
      val service = new UserService
      //调用add方法
      service.add()

  }
}

trait中定义具体的字段和抽象的字段

定义
  • 在trait中可以定义具体字段和抽象字段
  • 继承trait的子类自动拥有trait中定义的字段
  • 字段直接被添加到子类中
代码示例
object Test04 {
  //定义 日志 特质
  trait Logger {
    //定义一个SimpleDateFormat字段,用来格式化日期(显示到时间)
    val df = new SimpleDateFormat("yyyy-MM-dd HH:mm")
    //创建一个log抽象方法,用于输出日志
    def log(msg:String)
  }

  //创建ConsoleLogger类 转化当前时间并输出控制台信息
  class ConsoleLogger extends Logger {
    override def log(msg: String): Unit = {
      println(s"${df.format(new Date)}\t控制台消息:\n${msg}")
    }
  }

  def main(args: Array[String]): Unit = {
  	//创建对象
    val logger = new ConsoleLogger
    //调用方法
    logger.log("NullPointerException")
  }
}

模板模式

  1. 在一个特质中,具体方法依赖于抽象方法,而抽象方法可以放到继承trait的子类中实现,这种设计方式也称为模板模式
  2. 在scala中,trait是可以定义抽象方法,也可以定义具体方法的
  • trait中定义了一个抽象方法
  • trait中定义了其他的几个具体方法,会调用抽象方法
  • 其他实现类可以来实现抽象方法
  • 真正调用trait中具体方法的时候,其实会调用实现类的抽象方法实现
代码示例
object Test05 {
  //定义一个 日志 特质
  trait Logger {
    //定义一个日志信息抽象方法
    def log(msg:String)
    //定义 info方法
    def info(msg:String): Unit = log("INFO:" + msg)
    //定义 warn 方法
    def warn(msg:String): Unit = log("WARN:" + msg)
    //定义 error 方法
    def error(msg:String): Unit = log("ERROR:" + msg)
  }

  //定义控制台 输出日志
  class ConsoleLogger extends Logger {
    override def log(msg: String): Unit = {
      println(msg)
    }
  }

  def main(args: Array[String]): Unit = {
    //创建对象
    val logger = new ConsoleLogger
    //调用方法
    logger.info("信息日志")
    logger.warn("警告日志")
    logger.error("错误日志")
  }
}

对象混入trait

scala中可以将trait混入到对象中,就是将trait中定义的方法、字段添加到一个对象中

语法
val/var 对象名 = new 类 with 特质
代码示例
object Test06 {
  //定义一个日志 特质
  trait Logger {
    //输出日志的方法
    def log(msg:String): Unit = println(msg)
  }

  class UserService

  def main(args: Array[String]): Unit = {
    val service = new UserService with Logger
    service.log("混入的方法")
  }
}

trait调用链

类继承了多个trait后,可以依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法在最后都依次执行super关键字即可。类中调用多个tait中都有这个方法时,首先会从最右边的trait方法开始执行,然后依次往左执行,形成一个调用链条。

代码示例
object Test07 {
  //定义一个 处理 特质
  trait HandlerTrait {
    //定义一个处理 方法
    def handle(data:String): Unit = {
      println("处理数据....")
    }
  }

  //定义一个 数据验证的特质  继承 处理 特质
  trait DataValidHandlerTrait extends HandlerTrait {
    //重新处理 方法
    override def handle(data:String): Unit = {
      println("验证数据....")
      super.handle(data)
    }
  }

  //定义一个 签名校验 特质  继承处理特质
  trait SignatureValidHandlerTrait extends HandlerTrait {
    //重写处理方法
    override def handle(data: String): Unit = {
      println("效验签名...")
      super.handle(data)
    }
  }

  //定义一个支付 特质 继承 签名效验特质 和 数据验证特质
  class PayService extends DataValidHandlerTrait with SignatureValidHandlerTrait {
    //重写处理数据方法
    override def handle(data: String): Unit = {
      println("准备支付...")
      super.handle(data)
    }
  }

  def main(args: Array[String]): Unit = {
    val service = new PayService
    service.handle("支付参数")
  }
}

trait的构造机制

  • trait也有构造代码,但和类不一样,特质不能有构造器参数
  • 每个特质只有**一个无参数**的构造器。
  • 一个类继承另一个类、以及多个trait,当创建该类的实例时,它的构造顺序如下:
  1. 执行父类的构造器
  2. 从左到右依次执行trait的构造器
  3. 如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化一次
  4. 执行子类构造器

代码示例

object Test08 {
  trait Logger {
    println("执行Logger构造器")
  }

  trait MyLogger extends Logger {
    println("执行MyLogger构造器")
  }

  trait TimeLogger extends Logger {
    println("执行TimeLogger构造器")
  }

  class Person {
    println("执行Person构造器")
  }

  class Student extends Person with TimeLogger with MyLogger {
    println("执行Student构造器")
  }

  def main(args: Array[String]): Unit = {
    new Student
  }
}

在这里插入图片描述

trait继承class

trait也可以继承class的。特质会将class中的成员都继承下来。

代码示例
object Test16 {
  //创建一个MyUtils类,定义printMsg方法
  class MyUtil {
    def printMsg(msg:String): Unit = {
      println(msg)
    }
  }

  //创建一个Logger特质,继承自MyUtils,定义log方法
  trait Logger extends MyUtil {
    def log(msg:String): Unit = {
      printMsg("Logger:" +msg)
    }
  }

  //创建一个Person类 继承Logger类
  class Person extends Logger {
    //实现sayHello方法,调用log方法
    def sayHello(): Unit = {
      log("你好")
    }
  }

  def main(args: Array[String]): Unit = {
    val person = new Person
    person.sayHello()
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值