目录

特质(trait)

作为接口使用

定义具体的方法

定义具体方法和抽象方法

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

实例对象混入trait

trait调用链

trait的构造机制

trait继承class


特质(trait)

OLTP = online transaction processing

大数据:OLAP = online analysis processing

scala中没有interfact的接口

可以用trait来实现接口的功能。

同时trait比接口更强大

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

1. trait 中可以有 抽象的 也可以有 具体的
2. 一个类可以实现多个trait(通过with关键字)
3. 单个对象也可以附件多个trait
4. trait本身也可以继承其它class
通过这3个特性,我们可以体现出来。trait是一种 代码复用的最小单元
我们可以将想要的特性功能封装到trait中
不管是让class去附加 还是单个对象去附加 都OK
同时附加的数量不受到影响。

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

作为接口使用

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

案例1:继承单个trait

实现步骤:

创建一个Logger特质,添加一个接受一个String类型参数的log抽象方法

创建一个ConsoleLogger类,继承Logger特质,实现log方法,打印消息

添加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 ConsoleLoggerlogger.log("控制台日志: 这是一条Log")}
}

案例2:继承多个trait

实现步骤:

在上一个案例的基础上,创建一个MessageSender特质,添加接受一个String类型参数的send方法

再让ConsoleLogger实现一个MessageSender特质,并实现它的send方法,打印"发送消息..."

在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 ConsoleLoggerlogger.log("控制台日志: 这是一条Log")logger.send("你好!")}
}

案例3:object继承trait

实现步骤:

创建一个LoggerForObject特质,添加一个接受一个String类型参数的log抽象方法

创建一个ConsoleLogger的object,实现LoggerForObject特质,实现log方法,打印消息

编写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("程序退出")}
}

在trait中可以定义抽象方法,不写方法体就是抽象方法

和继承类一样,使用extends来继承trait

继承多个trait,使用with关键字

单例对象也可以继承trait

定义具体的方法

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

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

实现步骤:

1. 定义一个LoggerDetail特质

- 添加接受一个String类型的log方法,并打印参数

2. 定义一个UserService类,实现LoggerDetail特质

-  添加add方法,调用log方法打印"添加用户"

3. 添加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 UserServiceuserService.add()}
}

定义具体方法和抽象方法

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

案例:实现一个模板模式

实现步骤:

1. 添加一个LoggerFix特质

- 添加一个log抽象方法,接收String参数

- 添加一个info具体方法,接收String参数,调用log方法,参数添加"INFO:"

- 添加一个warn具体方法,接收String参数,调用log方法,参数添加"WARN:"

- 添加一个error具体方法,接收String参数,调用log方法,参数添加"ERROR:"

2. 创建ConsoleLoggerFix类

- 实现LoggerFix特质

- 实现log方法,打印参数

3. 添加main方法

- 创建ConsoleLoggerFix类对象

- 调用info方法

- 调用warn方法

- 调用error方法

示例代码:

trait Logger08 {// 抽象方法def log(msg:String)// 具体方法(该方法依赖于抽象方法logdef 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 ConsoleLogger08logger08.info("这是一条普通信息")logger08.warn("这是一条警告信息")logger08.error("这是一条错误信息")}
}

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

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

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

实现步骤:

1. 创建LoggerEx特质

- 定义一个sdf具体字段,类型为SimpleDateFormat,用来格式化日志(显示到时间)

- 创建一个INFO具体字段,类型为String,初始化为:"信息:当前日期"

- 创建一个TYPE抽象字段,类型为String

- 创建一个log抽象方法,参数为String类型

2. 创建ConsoleLoggerEx类

- 实现LoggerEx特质

- 实现TYPE抽象字段,初始化为"控制台"

- 实现log抽象方法,分别打印TYPE字段,INFO字段和参数

3. 添加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 ConsoleLoggerExlogger.log("这是一条消息")}
}

实例对象混入trait

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

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

实现步骤:

1. 创建一个LoggerMix混入

- 添加一个log方法,参数为String类型

- 打印参数

2. 创建一个UserService类

3. 添加main方法

- 创建UserService对象,还如LoggerMix特质

- 调用log方法

示例代码:

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

trait调用链 

责任链模式

需求:

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

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

实现步骤:

1. 定义一个HandlerTrait特质

- 定义一个具体的handler方法,接收String参数,打印"处理数据..."

2. 定义一个DataValidHandlerTrait,继承HandlerTrait特质

- 重写handler方法

- 打印"验证数据"

- 调用父特质的handler方法

3. 定义一个SignatureValidHandlerTrait,继承HandlerTrait特质

- 重写Handler方法

- 打印"检查签名"

- 调用父特质的handler方法

4. 创建一个PaymentService类

- 继承DataValidHandlerTrait

- 继承SignatureValidHandlerTrait

- 定义pay方法

  • 打印"准备支付"
  • 调用父特质的handler方法

5.添加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}")}
}// 程序运行输出如下:
// 准备支付...
// 检查签名...
// 验证数据...
// 处理数据...

trait的构造机制

责任链模式和类构造顺序不同

责任链(指的是对父类方法的调用):

  • 从右向左, 同层优先

父类构造顺序:

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

案例:演示trait的构造顺序

实现步骤:

  1. 创建一个Person_One特质,在构造器中打印"执行Person构造器!"
  2. 创建一个Logger_One特质,在构造器中打印"执行Logger构造器!"
  3. 创建一个MyLoggerOne特质,继承自LoggerOne特质,,在构造器中打印"执行MyLogger构造器!"
  4. 创建一个TimeLoggerOne特质,继承自LoggerOne特质,在构造器中打印"执行TimeLogger构造器!"
  5. 创建一个StudentOne类,继承自PersonOne、MyLoggerOne、TimeLoggerOne特质,在构造器中打印"执行Student构造器!"
  6. 添加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构造器!

trait继承class

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

单个实例对象附加trait的时候, 无法在任何地方调用trait父类的成员

必须是class继承trait才可以

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

实现步骤:

1. 创建一个MyUtils类

- 定义printMsg方法,接收String参数,并打印参数

2. 创建一个Logger_Two特质

- 继承自MyUtils

- 定义log方法,接收String参数,并打印一个前缀和参数

3. 创建一个Person_Three类

- 实现String类型name字段的主构造器

- 继承Logger_Two特质

- 实现sayHello方法

  • 调用log,传入"Hi, I‘m "加上name字段
  • 调用printMsg,传入"Hello, I'm "加上name字段

4. 添加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}
}

2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)相关推荐

  1. 2021年大数据常用语言Scala(三):Scala解释器

    目录 scala解释器 启动scala解释器 执行scala代码 退出解释器 scala解释器 后续我们会使用scala解释器来学习scala基本语法,scala解释器像Linux命令一样,执行一条代 ...

  2. 2021年大数据常用语言Scala(十四):基础语法学习 数组  重点掌握

    目录 数组  重点掌握 定长数组 变长数组 遍历数组 数组常用算法 数组  重点掌握 scala中数组的概念是和Java类似,可以用数组来存放一组数据.scala中,有两种数组,一种是定长数组,另一种 ...

  3. 2021年大数据常用语言Scala(四):基础语法学习 声明变量

    目录 声明变量 语法格式 在解释器中定义一个变量 val和var变量 使用类型推断来定义变量 惰性赋值 声明变量 我们将来每一天编写scala程序都会定义变量.那scala语言如何定义变量呢? 语法格 ...

  4. 2021年大数据常用语言Scala(一):Scala简介

    目录 一.Scala简介 为什么使用scala Scala对比Java 案例 一.Scala简介 scala是运行在JVM上的多范式编程语言,同时支持面向对象和面向函数编程 多范式:就是包含多种编程思 ...

  5. 2021年大数据常用语言Scala(二十八):scala面向对象 MAVEN依赖和类

    目录 scala面向对象 MAVEN依赖 类 - 掌握 创建类和对象 - 掌握 getter/setter - 了解 类的构造器 - 掌握 scala面向对象 MAVEN依赖 <?xml ver ...

  6. 2021年大数据常用语言Scala(二):Scala开发环境安装

    目录 开发环境安装 安装JDK 安装scala SDK 步骤 具体操作 安装IDEA scala插件 步骤 开发环境安装 学习如何编写scala代码之前,需要先安装scala编译器以及开发工具 sca ...

  7. 2021年大数据常用语言Scala(二十九):scala面向对象 单例对象

    目录 单例对象 定义object - 掌握 伴生对象 - 掌握 apply方法 - 掌握 main方法 单例对象 Scala中没有static关键字,但是它支持静态 如果要定义静态的东西,统统定义到o ...

  8. 2021年大数据常用语言Scala(十七):基础语法学习 Set

    目录 Set 不可变集 可变集 Set Set(集)是代表没有重复元素的集合.Set具备以下性质: 元素不重复 不保证插入顺序 和List正好相反, List: 元素可以重复 保证插入顺序 scala ...

  9. 2021年大数据常用语言Scala(三十八):scala高级用法 隐式转换和隐式参数

    目录 隐式转换和隐式参数 隐式转换 自动导入隐式转换方法 隐式转换的时机 隐式参数 隐式转换和隐式参数 隐式转换和隐式参数是scala非常有特色的功能,也是Java等其他编程语言没有的功能.我们可以很 ...

最新文章

  1. Web Service属性介绍
  2. qt可视化数据展板_Qt 2D数据可视化之QCharts
  3. Winforn中怎样在窗体中打开另一个窗体
  4. OSX 10.8+下开启Web 共享 的方法
  5. LeetCode动态规划 分割等和子集
  6. 深度分析Java的ClassLoader机制(源码级别)
  7. 电文的编码和译码c语言实现,电文的编码及译码.doc
  8. html一行显示四个图片,css一行显示之:实现多个图片一行显示的方法
  9. 使用js给数组去重的3种常用方法
  10. Python-win32com模块
  11. 基于matlab的自适应滤波器,基于MATLAB的自适应滤波器的设计与实现.doc
  12. cashfiesta网上赚钱
  13. 如何使用Git Pull覆盖本地文件
  14. MIT 线性代数导论 第十九、二十讲:行列式公式、代数余子式、克拉默法则
  15. WebGoat8 M17 Password Reset 密码重置 答案、思路、题解
  16. 开启usb调试的手机如何去除屏幕锁密码
  17. 倾斜摄影三维模型、激光点云、正射影像、数字高程模型如何实现在线浏览?
  18. blender2.8 bpy.data.images.new创建的图片返回值撤回操作后丢失
  19. 计算机应用专业顶岗实习计划,计算机学生顶岗实习计划(网络版)
  20. 【转】 机器学习入门——浅谈神经网络

热门文章

  1. 2022-2028年中国汽车修理行业市场前瞻与投资规划分析报告
  2. SpringCloud Alibaba微服务实战(二) - Nacos服务注册与restTemplate消费
  3. torch.nn.Embedding
  4. Jackson、FastJson快速入门(整合SpringMVC)
  5. MLIR(Multi-Level Intermediate Representation Compiler)架构 Infrastructure
  6. MegEngine基本概念
  7. NSight Compute 用户手册(中)
  8. 电商商品模块数据设计与关系图
  9. [微信官方文档] 小程序-错误码信息与解决方案表
  10. java模拟网银登录_用java编写模拟网上银行登录及存取款业务