大数据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!"
}

原文地址:https://blog.51cto.com/14573321/2444226

时间: 2024-11-09 00:33:01

大数据Scala系列之特质的相关文章

大数据Scala系列之文件以及正则表达式

大数据Scala系列之文件以及正则表达式1 读取行导入scala.io.Source后,即可引用Source中的方法读取文件信息. import scala.io.Sourceobject FileDemo extends App{val source = Source.fromFile("C:/Users/admin/res.txt")//返回一个迭代器val lines = source.getLines()for(i <- lines)println(i)//内容也可以放到

大数据Scala编程.问题集(03)

by 高焕堂 洞庭国际智能硬件检测基地 & 中云大数据中心(IDC) 首席架构师 微博:@高焕堂_台北 Q-03: 如何从TemplateMethod设计模式理解trait的两项接口? Answer: Scala的trait就是接口类(Interface class),其意味着,它的结构是抽象类,但其扮演接口的角色.通常,可从有两个不同视角去看待trait机制: l 第一个视角:将trait对应到Java的"Interface"机制.此时,将trait看成一个接口(主要是抽象函

大数据学习系列之五 ----- Hive整合HBase图文详解

引言 在上一篇 大数据学习系列之四 ----- Hadoop+Hive环境搭建图文详解(单机) 和之前的大数据学习系列之二 ----- HBase环境搭建(单机) 中成功搭建了Hive和HBase的环境,并进行了相应的测试.本文主要讲的是如何将Hive和HBase进行整合. Hive和HBase的通信意图 Hive与HBase整合的实现是利用两者本身对外的API接口互相通信来完成的,其具体工作交由Hive的lib目录中的hive-hbase-handler-*.jar工具类来实现,通信原理如下图

大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解

引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单机的搭建,是因为作为个人学习的话,单机已足以,好吧,说实话是自己的电脑不行,使用虚拟机实在太卡了... 整个的集群搭建是在公司的测试服务搭建的,在搭建的时候遇到各种各样的坑,当然也收获颇多.在成功搭建大数据集群之后,零零散散的做了写笔记,然后重新将这些笔记整理了下来.于是就有了本篇博文. 其实我在搭

大数据学习系列之六 ----- Hadoop+Spark环境搭建

引言 在上一篇中 大数据学习系列之五 ----- Hive整合HBase图文详解 : http://www.panchengming.com/2017/12/18/pancm62/ 中使用Hive整合HBase,并且测试成功了.在之前的大数据学习系列之一 ----- Hadoop环境搭建(单机) : http://www.panchengming.com/2017/11/26/pancm55/ 中成功的搭建了Hadoop的环境,本文主要讲的是Hadoop+Spark 的环境.虽然搭建的是单机版,

大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集

引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单机的搭建,是因为作为个人学习的话,单机已足以,好吧,说实话是自己的电脑不行,使用虚拟机实在太卡了... 整个的集群搭建是在公司的测试服务搭建的,在搭建的时候遇到各种各样的坑,当然也收获颇多.在成功搭建大数据集群之后,零零散散的做了写笔记,然后重新将这些笔记整理了下来.于是就有了本篇博文. 其实我在搭

ambari 搭建hadoop大数据平台系列4-配置ambari-server

ambari 搭建hadoop大数据平台系列4-配置ambari-server,分为三部分: 官网:  https://docs.hortonworks.com/HDPDocuments/Ambari-2.4.2.0/bk_ambari-installation/content/download_the_ambari_repo_lnx7.html 安装ambari-server  配置ambari-server  命令;ambari-server setup 启动ambari-server 命令

大数据Scala编程.问题集(02)

by 高焕堂 洞庭国际智能硬件检测基地 & 中云大数据中心(IDC) 首席架构师 微博:@高焕堂_台北 Q-02: Scala语言的trait具有什么设计涵意? Answer: 大家都知道接口(Interface)的概念,也知道一个类(Class)或一个模块(Module)能实现多个接口.就像一个房间可以有多个门,或一座四合院可以有多个门口一样.如下图: 将四合院的概念对应到软件上,一个软件的类可以实践多个接口,如下图: 现在,先拿一个类和一个接口的设计架构来看看,如下图: 在一般软件设计上,接

【PPT&amp;视频】《陈新河:万亿元大数据产业新生态》——央视网大数据名人讲堂之大数据产业系列

[PPT&视频]<陈新河:万亿元大数据产业新生态>--央视网大数据名人讲堂之大数据产业系列 原创 2016-07-16 陈新河 软件定义世界(SDX) 热门下载(点击标题即可阅读) ?[下载]2015中国数据分析师行业峰会精彩PPT下载(共计21个文件) 因微信限制,部分图不能显示出来,高清完整版全文请扫描二维码,见每篇文章底部专栏 <陈新河:万亿元大数据产业新生态>--央视网大数据名人讲堂之大数据产业系列 嘉宾介绍 陈新河   中关村大数据产业联盟副秘书长 Talking