7.大数据技术之Scala_十二章:特质

十二  特质

12.1  不允许多重集成

所有的面向对象的语言都不允许直接的多重继承,因为会出现“deadly diamond of death”问题。Scala提供了特质(trait),特质可以同时拥有抽象方法和具体方法,一个类可以实现多个特质。

12.2  当做接口使用的特质

特质中没有实现的方法就是抽象方法。类通过extends继承特质,通过with可以继承多个特质。

trait Logger {

  def log(msg: String)

}

 

class ConsoleLogger extends Logger with Cloneable with Serializable {

  def log(msg: String) {

    println(msg)

  }

}

Logger with Cloneable with Serializable是一个整体,extends这个整体

所有的java接口都可以当做Scala特质使用。

12.3  带有具体实现的特质

特质中的方法并不一定是抽象的:

trait ConsoleLogger {

  def log(msg: String) {

    println(msg)

  }

}

 

class Account {

  protected var balance = 0.0

}

 

class drawAccount extends Account with ConsoleLogger {

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

12.4  带有特质的对象,动态混入

在构建对象时混入某个具体的特质,覆盖掉抽象方法,提供具体实现:

trait Logger {

  def log(msg: String)

}

 

trait ConsoleLogger extends Logger {

  def log(msg: String) {

    println(msg)

  }

}

 

class Account {

  protected var balance = 0.0

}

 

abstract class SavingsAccount extends Account with Logger {

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

 

object Main extends App {

  val account = new SavingsAccount with ConsoleLogger

  account.withdraw(100)

}

12.5  叠加在一起的特质

super并不是指继承关系,而是指的加载顺序。

继承多个相同父特质的类,会从右到左依次调用特质的方法。Super指的是继承特质左边的特质,从源码是无法判断super.method会执行哪里的方法,如果想要调用具体特质的方法,可以指定:super[ConsoleLogger].log(…).其中的泛型必须是该特质的直接超类类型

trait Logger {

  def log(msg: String);

}

 

trait ConsoleLogger extends Logger {

  def log(msg: String) {

    println(msg)

  }

}

 

trait TimestampLogger extends ConsoleLogger {

  override def log(msg: String) {

    super.log(new java.util.Date() + " " + msg)

  }

}

 

trait ShortLogger extends ConsoleLogger {

  override def log(msg: String) {

    super.log(if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...")

  }

}

 

class Account {

  protected var balance = 0.0

}

 

abstract class SavingsAccount extends Account with Logger {

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

 

object Main extends App {

  val acct1 = new SavingsAccount with TimestampLogger with ShortLogger

  val acct2 = new SavingsAccount with ShortLogger with TimestampLogger

  acct1.withdraw(100)

  acct2.withdraw(100)

}

12.6  在特质中重写抽象方法

trait Logger2 {

  def log(msg: String)

}

 

//因为有super,Scala认为log还是一个抽象方法

trait TimestampLogger2 extends Logger2 {

  abstract override def log(msg: String) {

    super.log(new java.util.Date() + " " + msg)

  }

}

 

trait ShortLogger2 extends Logger2 {

  abstract override def log(msg: String) {

    super.log(if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...")

  }

}

 

trait ConsoleLogger2 extends Logger2 {

  override def log(msg: String) {

    println(msg)

  }

}

 

class Account2 {

  protected var balance = 0.0

}

 

abstract class SavingsAccount2 extends Account2 with Logger2 {

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

 

object Main2 extends App {

  //这里可以根据12.5的知识点理解此处

  val acct1 = new SavingsAccount2 with ConsoleLogger2 with TimestampLogger2 with ShortLogger2

  acct1.withdraw(100)

}

12.7  当做富接口使用的特质

即该特质中既有抽象方法,又有非抽象方法

//富特质

trait Logger3 {

  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)

  }

}

 

trait ConsoleLogger3 extends Logger3 {

  def log(msg: String) {

    println(msg)

  }

}

 

class Account3 {

  protected var balance = 0.0

}

 

abstract class SavingsAccount3 extends Account3 with Logger3 {

  def withdraw(amount: Double) {

    if (amount > balance) severe("余额不足")

    else balance -= amount

  }

}

 

object Main3 extends App {

  val acct = new SavingsAccount3 with ConsoleLogger3

  acct.withdraw(100)

}

12.8  特质中的具体字段

特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。

混入该特质的类就具有了该字段,字段不是继承,而是简单的加入类。是自己的字段。

trait Logger4 {

  def log(msg: String)

}

 

trait ConsoleLogger4 extends Logger4 {

  def log(msg: String) {

    println(msg)

  }

}

 

trait ShortLogger4 extends Logger4 {

  val maxLength = 15

  abstract override def log(msg: String) {

    super.log(if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...")

  }

}

 

class Account4 {

  protected var balance = 0.0

}

 

class SavingsAccount4 extends Account4 with ConsoleLogger4 with ShortLogger4 {

  var interest = 0.0

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

 

object Main4 extends App {

  val acct = new SavingsAccount4

  acct.withdraw(100)

  println(acct.maxLength)

}

12.9  特质中的抽象字段

特质中未被初始化的字段在具体的子类中必须被重写。

//特质中的具体字段

trait Logger5 {

  def log(msg: String)

}

 

trait ConsoleLogger5 extends Logger5 {

  def log(msg: String) {

    println(msg)

  }

}

 

trait ShortLogger5 extends Logger5 {

  val maxLength: Int

 

  abstract override def log(msg: String) {

    super.log(if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...")

  }

}

 

class Account5 {

  protected var balance = 0.0

}

 

abstract class SavingsAccount5 extends Account5 with Logger5 {

  var interest = 0.0

 

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

 

object Main5 extends App {

  val acct = new SavingsAccount5 with ConsoleLogger5 with ShortLogger5 {

    val maxLength = 20

  }

  acct.withdraw(100)

  println(acct.maxLength)

}

12.10  特质构造顺序

特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成

trait Logger6 {

  println("我在Logger6特质构造器中,嘿嘿嘿。。。")

  def log(msg: String)

}

 

trait ConsoleLogger6 extends Logger6 {

  println("我在ConsoleLogger6特质构造器中,嘿嘿嘿。。。")

  def log(msg: String) {

    println(msg)

  }

}

 

trait ShortLogger6 extends Logger6 {

  val maxLength: Int

  println("我在ShortLogger6特质构造器中,嘿嘿嘿。。。")

 

  abstract override def log(msg: String) {

    super.log(if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...")

  }

}

 

class Account6 {

  println("我在Account6构造器中,嘿嘿嘿。。。")

  protected var balance = 0.0

}

 

abstract class SavingsAccount6 extends Account6 with ConsoleLogger6 with ShortLogger6{

  println("我再SavingsAccount6构造器中")

  var interest = 0.0

  override val maxLength: Int = 20

  def withdraw(amount: Double) {

    if (amount > balance) log("余额不足")

    else balance -= amount

  }

}

 

object Main6 extends App {

  val acct = new SavingsAccount6 with ConsoleLogger6 with ShortLogger6

  acct.withdraw(100)

  println(acct.maxLength)

}

步骤总结:

1、调用当前类的超类构造器

2、第一个特质的父特质构造器

3、第一个特质构造器

4、第二个特质构造器的父特质构造器由于已经执行完成,所以不再执行

5、第二个特质构造器

6、当前类构造器

12.11  初始化特质中的字段

特质不能有构造器参数,每个特质都有一个无参数的构造器。缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有特性,比如具体的和抽象的字段,以及超类。现在有如下情景:我们想通过特质来实现日志数据的输出,输出到某一个文件中

import java.io.PrintStream

 

 

trait Logger7{

  def log(msg:String)

}

 

trait FileLogger7 extends Logger7{

  val fileName:String

  val out = new PrintStream(fileName)

 

  override def log(msg: String): Unit = {

    out.print(msg)

    out.flush()

  }

}

 

class SavingsAccount7{

 

}

 

object Main7 extends App {

  val acct = new SavingsAccount7 with FileLogger7 {

    override val fileName = "2017-11-24.log"//空指针异常

  }

}

如果想修复如上错误,可以:

1)  使用“提前定义”

import java.io.PrintStream

 

trait Logger7 {

  def log(msg: String)

}

 

trait FileLogger7 extends Logger7 {

  val fileName: String

  val out = new PrintStream(fileName)

 

  override def log(msg: String): Unit = {

    out.print(msg)

    out.flush()

  }

}

 

class SavingsAccount7 {

 

}

 

object Main7 extends App {

  //提前定义

  val acct = new {

    override val fileName = "2017-11-24.log"

  } with SavingsAccount7 with FileLogger7

  acct.log("heiheihei")

}

或这样提前定义:

import java.io.PrintStream

 

trait Logger7 {

  def log(msg: String)

}

 

trait FileLogger7 extends Logger7 {

  val fileName: String

  val out = new PrintStream(fileName)

 

  override def log(msg: String): Unit = {

    out.print(msg)

    out.flush()

  }

}

 

//提前定义在这里

class SavingsAccount7 extends {

  override val fileName = "2017-11-24.log"

} with FileLogger7

 

object Main7 extends App {

  val acct = new SavingsAccount7 with FileLogger7

  acct.log("嘿嘿嘿")

}

2)  使用lazy

import java.io.PrintStream

 

trait Logger7 {

  def log(msg: String)

}

 

trait FileLogger7 extends Logger7 {

  val fileName: String

  lazy val out = new PrintStream(fileName)

 

  override def log(msg: String): Unit = {

    out.print(msg)

    out.flush()

  }

}

 

class SavingsAccount7 {

 

}

 

object Main7 extends App {

  val acct = new SavingsAccount7 with FileLogger7 {

    override val fileName = "2017-11-24.log"

  }

  acct.log("哈哈哈")

}

 

12.12  扩展类的特质

总结:

1、特质可以继承自类,以用来拓展该类的一些功能

2、所有混入该特质的类,会自动成为那个特质所继承的超类的子类

3、如果混入该特质的类,已经继承了另一个类,不就矛盾了?注意,只要继承的那个类是特质超类的子类即可。

例如:

1) 特质可以继承自类,以用来拓展该类的一些功能

trait LoggedException extends Exception{

  def log(): Unit ={

    println(getMessage())

  }

}

2) 所有混入该特质的类,会自动成为那个特质所继承的超类的子类

class UnhappyException extends LoggedException{

  override def getMessage = "哦,我的上帝,我要踢爆他的屁股!"

}

3)  如果混入该特质的类,已经继承了另一个类,不就矛盾了?注意,只要继承的那个类是特质超类的子类即可。

正确:

class UnhappyException2 extends IndexOutOfBoundsException with LoggedException{

  override def getMessage = "哦,我的上帝,我要踢爆他的屁股!"

}

错误:

class UnhappyException3 extends JFrame with LoggedException{

  override def getMessage = "哦,我的上帝,我要踢爆他的屁股!"

}

12.13  自身类型

主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。

比如:

//自身类型特质

trait Logger9{

  this: Exception =>

  def log(): Unit ={

    println(getMessage)

  }

}

这样一来,在该特质中,可以随意调用“自身类型”中的各种方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值