Scala面向对象3

本文涉及的产品
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: Scala面向对象3

4 特质(trait)

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

接下来,我们就来学习trait的几种用法。

7.1 作为接口使用


  • 使用extends来继承trait(scala不论是类还是特质,都是使用extends关键字)
  • 如果要继承多个trait,则使用with关键字

案例1:继承单个trait

实现步骤:

  1. 创建一个Logger特质,添加一个接受一个String类型参数的log抽象方法
  1. 创建一个ConsoleLogger类,继承Logger特质,实现log方法,打印消息
  2. 添加main方法,创建ConsoleLogger对象,调用log方法

示例代码:

trait Logger {
  // 抽象方法
  def log(msg:String)
}
class ConsoleLogger extends Logger {
  override def log(msg: String): Unit = println(msg)
}
object LoggerTrait {
  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLogger
    logger.log("控制台日志: 这是一条Log")
  }
}

案例2:继承多个trait

实现步骤:

  1. 在上一个案例的基础上,创建一个MessageSender特质,添加接受一个String类型参数的send方法
  1. 再让ConsoleLogger实现一个MessageSender特质,并实现它的send方法,打印"发送消息…"
  2. 在main中调用,send方法

示例代码:

trait Logger {
  // 抽象方法
  def log(msg:String)
}
trait MessageSender {
  def send(msg:String)
}
class ConsoleLogger extends Logger with MessageSender {
  override def log(msg: String): Unit = println(msg)
  override def send(msg: String): Unit = println(s"发送消息:${msg}")
}
object LoggerTrait {
  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLogger
    logger.log("控制台日志: 这是一条Log")
    logger.send("你好!")
  }
}

案例3:object继承trait

实现步骤:

  1. 创建一个LoggerForObject特质,添加一个接受一个String类型参数的log抽象方法
  2. 创建一个ConsoleLogger的object,实现LoggerForObject特质,实现log方法,打印消息
  3. 编写main方法,调用ConsoleLogger的log方法
trait LoggerForObject {
  // 抽象方法
  def log(msg:String)
}
// object也可以继承trait
object ConsoleLogger extends LoggerForObject {
  override def log(msg: String): Unit = println(s"控制台信息 $msg")
}
object ObjectTrait {
  def main(args: Array[String]): Unit = {
    ConsoleLogger.log("程序退出")
  }
}
  1. 在trait中可以定义抽象方法,不写方法体就是抽象方法
  2. 和继承类一样,使用extends来继承trait
  3. 继承多个trait,使用with关键字
  4. 单例对象也可以继承trait

4.2 定义具体的方法

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

案例:在trait中定义具体方法

实现步骤:

  1. 定义一个LoggerDetail特质
  • 添加接受一个String类型的log方法,并打印参数
  1. 定义一个UserService类,实现LoggerDetail特质
  • 添加add方法,调用log方法打印"添加用户"
  1. 添加main方法
  • 创建UserService对象实例
  • 调用add方法

示例代码:

trait LoggerDetail {
  // 在trait中定义具体方法
  def log(msg:String) = println(msg)
}
class UserService extends LoggerDetail {
  def add() = log("添加用户")
}
object MethodInTrait {
  def main(args: Array[String]): Unit = {
    val userService = new UserService
    userService.add()
  }
}

4.3 定义具体方法和抽象方法


  • 在trait中,可以混合使用具体方法和抽象方法
  • 使用具体方法依赖于抽象方法,而抽象方法可以放到继承trait的子类中实现,这种设计方式也称为模板模式

案例:实现一个模板模式

实现步骤:

  1. 添加一个LoggerFix特质
  • 添加一个log抽象方法,接收String参数
  • 添加一个info具体方法,接收String参数,调用log方法,参数添加"INFO:"
  • 添加一个warn具体方法,接收String参数,调用log方法,参数添加"WARN:"
  • 添加一个error具体方法,接收String参数,调用log方法,参数添加"ERROR:"
  1. 创建ConsoleLoggerFix类
  • 实现LoggerFix特质
  • 实现log方法,打印参数
  1. 添加main方法
  • 创建ConsoleLoggerFix类对象
  • 调用info方法
  • 调用warn方法
  • 调用error方法

示例代码:

trait Logger08 {
  // 抽象方法
  def log(msg:String)
  // 具体方法(该方法依赖于抽象方法log
  def info(msg:String) = log("INFO:" + msg)
  def warn(msg:String) = log("WARN:" + msg)
  def error(msg:String) = log("ERROR:" + msg)
}
class ConsoleLogger08 extends Logger08 {
  override def log(msg: String): Unit = println(msg)
}
object Trait08 {
  def main(args: Array[String]): Unit = {
    val logger08 = new ConsoleLogger08
    logger08.info("这是一条普通信息")
    logger08.warn("这是一条警告信息")
    logger08.error("这是一条错误信息")
  }
}

4.4 定义具体的字段和抽象的字段


  • 在trait中可以定义具体字段和抽象字段
  • 继承trait的子类自动拥有trait中定义的字段
  • 字段直接被添加到子类中

案例:在trait中定义具体的字段和抽象的字段

实现步骤:

  1. 创建LoggerEx特质
  • 定义一个sdf具体字段,类型为SimpleDateFormat,用来格式化日志(显示到时间)
  • 创建一个INFO具体字段,类型为String,初始化为:“信息:当前日期”
  • 创建一个TYPE抽象字段,类型为String
  • 创建一个log抽象方法,参数为String类型
  1. 创建ConsoleLoggerEx类
  • 实现LoggerEx特质
  • 实现TYPE抽象字段,初始化为"控制台"
  • 实现log抽象方法,分别打印TYPE字段,INFO字段和参数
  1. 添加main方法
  • 创建ConsoleLoggerEx类对象
  • 调用log方法

示例代码:

trait LoggerEx {
  // 具体字段
  val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")
  val INFO = "信息:" + sdf.format(new Date)
  // 抽象字段
  val TYPE:String
  // 抽象方法
  def log(msg:String)
}
class ConsoleLoggerEx extends LoggerEx {
  // 实现抽象字段
  override val TYPE: String = "控制台"
  // 实现抽象方法
  override def log(msg:String): Unit = print(s"$TYPE$INFO $msg")
}
object FieldInTrait {
  def main(args: Array[String]): Unit = {
    val logger = new ConsoleLoggerEx
    logger.log("这是一条消息")
  }
}

4.5 实例对象混入trait


  • trait还可以混入到实例对象中,给对象实例添加额外的行为
  • 只有混入了trait的对象才具有trait中的方法,其他的类对象不具有trait中的行为
  • 使用with将trait混入到实例对象中

案例:将一个特质混入到一个对象中

实现步骤:

  1. 创建一个LoggerMix混入
  • 添加一个log方法,参数为String类型
  • 打印参数
  1. 创建一个UserService类
  2. 添加main方法
  • 创建UserService对象,还如LoggerMix特质
  • 调用log方法

示例代码:

trait LoggerMix {
  def log(msg:String) = println(msg)
}
class UserService
object FixedInClass {
  def main(args: Array[String]): Unit = {
    // 使用with关键字直接将特质混入到对象中
    val userService = new UserService with LoggerMix
    userService.log("你好")
  }
}

4.6 trait调用链


责任链模式

需求:

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

案例:实现一个模拟支付过程的调用链

实现步骤:

  1. 定义一个HandlerTrait特质
  • 定义一个具体的handler方法,接收String参数,打印"处理数据…"
  1. 定义一个DataValidHandlerTrait,继承HandlerTrait特质
  • 重写handler方法
  • 打印"验证数据"
  • 调用父特质的handler方法
  1. 定义一个SignatureValidHandlerTrait,继承HandlerTrait特质
  • 重写Handler方法
  • 打印"检查签名"
  • 调用父特质的handler方法
  1. 创建一个PaymentService类
  • 继承DataValidHandlerTrait
  • 继承SignatureValidHandlerTrait
  • 定义pay方法
  • 打印"准备支付"
  • 调用父特质的handler方法
  1. 添加main方法
  • 创建PaymentService对象实例
  • 调用pay方法

示例:

// 支付数据处理
trait HandlerTrait {
  def handle(data: String) = {
    println("处理数据...")
  }
}
// 数据校验处理
trait DataValidHandlerTrait extends HandlerTrait {
  override def handle(data: String) = {
    println("验证数据...")
    super.handle(data)
  }
}
// 签名校验处理
trait SignatureValidHandlerTrait extends HandlerTrait {
  override def handle(data: String) = {
    println("检查签名...")
    super.handle(data)
  }
}
// 支付服务
class PaymentService extends DataValidHandlerTrait with SignatureValidHandlerTrait {
  def pay(data:String) = {
    println("准备支付...")
    this.handle(data)
  }
}
object PaymentService {
  def main(args: Array[String]) {
    val payService = new PaymentService()
    payService.pay("signature:10233123||md5:123889a3d5s1f6123||data:{order:001,money:200}")
  }
}
// 程序运行输出如下:
// 准备支付...
// 检查签名...
// 验证数据...
// 处理数据...

4.7 trait的构造机制


  • trait也有构造代码,但和类不一样,特质不能有构造器参数
  • 每个特质只有**一个无参数**的构造器。

一个类继承另一个类、以及多个trait,当创建该类的实例时,它的构造顺序如下:

  1. 执行父类的构造器
  2. 从左到右依次执行trait的构造器
  1. 如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化一次
  2. 执行子类构造器

案例:演示trait的构造顺序

实现步骤:

  1. 创建一个Person_One特质,在构造器中打印"执行Person构造器!"
  2. 创建一个Logger_One特质,在构造器中打印"执行Logger构造器!"
  3. 创建一个MyLogger_One特质,继承自Logger_One特质,,在构造器中打印"执行MyLogger构造器!"
  4. 创建一个TimeLogger_One特质,继承自Logger_One特质,在构造器中打印"执行TimeLogger构造器!"
  1. 创建一个Student_One类,继承自Person_One、MyLogger_One、TimeLogger_One特质,在构造器中打印"执行Student构造器!"
  2. 添加main方法,实例化Student_One类,观察输出。

示例代码:

class Person_One {
  println("执行Person构造器!")
}
trait Logger_One {
  println("执行Logger构造器!")
}
trait MyLogger_One extends Logger_One {
  println("执行MyLogger构造器!")
}
trait TimeLogger_One extends Logger_One {
  println("执行TimeLogger构造器!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
  println("执行Student构造器!")
  }
object exe_one {
  def main(args: Array[String]): Unit = {
    val student = new Student_One
  }
}
// 程序运行输出如下:
// 执行Person构造器!
// 执行Logger构造器!
// 执行MyLogger构造器!
// 执行TimeLogger构造器!
// 执行Student构造器!

4.8 trait继承class


  • trait也可以继承class
  • 这个class就会成为所有该trait子类的超级父类。

案例:定义一个特质,继承自一个class

实现步骤:

  1. 创建一个MyUtils类
  • 定义printMsg方法,接收String参数,并打印参数
  1. 创建一个Logger_Two特质
  • 继承自MyUtils
  • 定义log方法,接收String参数,并打印一个前缀和参数
  1. 创建一个Person_Three类
  • 实现String类型name字段的主构造器
  • 继承Logger_Two特质
  • 实现sayHello方法
  • 调用log,传入"Hi, I‘m "加上name字段
  • 调用printMsg,传入"Hello, I’m "加上name字段
  1. 添加main方法
  • 创建一个Person_Three对象
  • 调用sayHello方法

示例:

class MyUtil {
  def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
  def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
    def sayHello {
        this.log("Hi, I'm " + this.name)
        this.printMsg("Hello, I'm " + this.name)
  }
}
object Person_Three{
  def main(args: Array[String]) {
      val p=new Person_Three("Tom")
      p.sayHello
    //执行结果:
//      log: Hi, I'm Tom
//      Hello, I'm Tom
  }
}


相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3 )前置知识要求   课程大纲 第一章 了解数据仓库概念 初步了解数据仓库是干什么的 第二章 按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章 数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章 采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章 用户行为数据仓库 严格按照企业的标准开发 第六章 搭建业务数仓理论基础和对表的分类同步 第七章 业务数仓的搭建  业务行为数仓效果图  
目录
相关文章
|
7月前
|
设计模式 Java Scala
Scala 面向对象【中】
Scala 面向对象【中】
|
7月前
|
Java Scala Python
Scala面向对象【上】
Scala面向对象【上】
|
7月前
|
Java Scala
Scala面向对象【下】
Scala面向对象【下】
|
7月前
|
分布式计算 Java Scala
Scala:面向对象、Object、抽象类、内部类、特质Trait(二)
Scala:面向对象、Object、抽象类、内部类、特质Trait(二)
100 0
|
7月前
|
大数据 Scala
Scala面向对象练习题34道
Scala面向对象练习题34道
139 0
|
Java Scala
Scala面向对象4
Scala面向对象4
54 0
|
分布式计算 Java Scala
Scala面向对象2
Scala面向对象2
68 0
|
Java 编译器 Scala
Scala面向对象1
Scala面向对象1
69 0
|
SQL JSON 前端开发
Scala的面向对象与函数编程
Scala的面向对象与函数编程
Scala的面向对象与函数编程
|
存储 算法 Java
Scala学习三-面向对象
前面我们已经学习了特质类似接口,其可以被继承,同时如果需要继承多个特质的话,则需要使用extends…with…进行继承。其类似java中的接口和抽象方法的结合体,但又比java中的其要强大,因为其可以定义抽象字段和普通字段、抽象方法和普通方法。而在java中接口中可以定义常量,不能定义变量。同时特质还可以继承class类,而在java中接口通常是用来实现的。 Object继承trait
139 0
Scala学习三-面向对象