zoukankan      html  css  js  c++  java
  • 11.Scala-特质

    第11章 特质

    11.1 不允许多重继承

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

    11.2 当做接口使用的特质

    特质中没有实现的方法就是抽象方法。
    类通过 extends 继承特质,通过 with 可以继承多个特质。
     
    trait Logger {
      def log(msg: String) // An abstract method
    }


    class ConsoleLogger extends Logger with Cloneable with Serializable { // Use extends, not implements. Use with to add multiple traits   def log(msg: String) {
        println(msg)
      } // No override needed }
    Logger with Cloneable with Serializable 是一个整体,extends 这个整体
    所有的 java 接口都可以当做 Scala 特质使用。

    11.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("Insufficient funds")
        else balance -= amount
      }
      // More methods ...
    }
    使用上:使 DrawAccount 得到了一个具体的 log 方法(minix)混入,
    并不是通过继承得来。

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

    在构建对象时混入某个具体的特质,覆盖掉抽象方法,提供具体实现。
    trait Logger {
      def log(msg: String);
    }

    // 继承 Logger 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("Insufficient funds") else balance -= amount } // More methods ... }

    object Main
    extends App { val acct = new SavingsAccount with ConsoleLogger //动态混入 acct.withdraw(100) }

    /*
      * 当前 new 的这个类,没有实现这个类所继承的带有抽象方法的特质,这个时候,可以在 new 的同时,混入一个已经实现该特质抽象方法的特质。
    */
     

    11.5 叠加在一起的特质

    super 并不是指继承关系,而是指的加载顺序。
    继承多个相同父特质的类,会从右到左依次调用特质的方法。Super 指的
    是继承特质左边的特质,从源码是无法判断 super.method 会执行哪里的方法,
    如果想要调用具体特质的方法,可以指定:super[ConsoleLogger].log(….).
    其中的泛型必须是该特质的直接超类类型
    /**
      * 叠加特质的执行顺序
      * 1、动态混入:从右向左执行
      * 2、情景“混入的多个特质,是继承了同一个特质
      * 3、super 关键字为向左调用
      * 4、如果左边没有特质了,则调用父特质中的方法
     */
    
    trait Logger3 {
      def log(msg: String);
    }
    
    
    // 继承 Logger 特质,提供具体的 log 方法
    trait ConsoleLogger3 extends Logger3 {
      def log(msg: String) { println(msg) }
    }
    
    
    // 注意 super
    trait TimestampLogger3 extends ConsoleLogger3 {
      override def log(msg: String) {
        super.log(new java.util.Date() + " " + msg)
      }
    }
    
    
    trait ShortLogger3 extends ConsoleLogger3 {
      override def log(msg: String) {
        super.log(
          if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...")
      }
    }
    
    
    class Account3 {
      protected var balance = 0.0
    }
    
    
    abstract class SavingsAccount3 extends Account3 with Logger3 {
      def withdraw(amount: Double) {
        if (amount > balance) log("Insufficient funds")
        else balance -= amount
      }
      // More methods ...
    }
    
    
    object Main3 extends App {
    
      val acct1 = new SavingsAccount3 with TimestampLogger3 with ShortLogger3
      acct1.withdraw(100) //Thu Jul 04 00:23:24 CST 2019 Insufficient...
    
      val acct2 = new SavingsAccount3 with ShortLogger3 with TimestampLogger3
      acct2.withdraw(100) //Thu Jul 04 0...
    
    }

    11.6 在特质中重写抽象方法

    trait Logger4 {
      def log(msg: String) // This method is abstract
    }
    
    
    //因为有 super,Scala 认为 log 还是一个抽象方法
    trait TimestampLogger4 extends Logger4 {
      abstract override def log(msg: String) {
        super.log(new java.util.Date() + " " + msg)
      }
    }
    
    
    trait ShortLogger4 extends Logger4 {
      abstract override def log(msg: String) {
        super.log(
          if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...")
      }
    }
    
    
    trait ConsoleLogger4 extends Logger4 {
      override def log(msg: String) { println(msg) }
    }
    
    
    class Account4 {
      protected var balance = 0.0
    }
    
    
    abstract class SavingsAccount4 extends Account4 with Logger4 {
      def withdraw(amount: Double) {
        if (amount > balance) log("Insufficient funds")
        else balance -= amount
      }
      // More methods ...
    }
    
    
    object Main4 extends App {
      val acct1 = new SavingsAccount4 with ConsoleLogger4 with TimestampLogger4 with ShortLogger4
      acct1.withdraw(100)
    }
    TimestampLogger 重写了 Logger 的抽象方法,提供了新的抽象方法。

    11.7 当做富接口使用的特质

    富接口:即该特质中既有抽象方法,又有非抽象方法
    //当作富接口使用的特质
    trait Logger5 { //这是一个富特质
      //抽象方法
      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 ConsoleLogger5 extends Logger5 {
      def log(msg: String) {
        println(msg)
      }
    }
    
    
    class Account5 {
      protected var balance = 0.0
    }
    
    
    abstract class SavingsAccount5 extends Account5 with Logger5 {
      def withdraw(amount: Double) {
        if (amount > balance) severe("Insufficient funds")
        else balance -= amount
      }
      // More methods ...
    }
    
    
    object Main5 extends App {
      val acct = new SavingsAccount5 with ConsoleLogger5
      acct.withdraw(100) //SEVERE: Insufficient funds
    }

    11.8 特质中的具体字段

      特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化
    就是抽象字段。
      混入该特质的类就具有了该字段,字段不是继承,而是简单的加入类。
    是自己的字段
    trait Logger6 {
      def log(msg: String)
    }
    
    
    trait ConsoleLogger6 extends Logger6 {
      def log(msg: String) { println(msg) }
    }
    
    
    trait ShortLogger6 extends Logger6 {
      val maxLength = 15
      abstract override def log(msg: String) {
        super.log(
          if (msg.length <= maxLength) msg
          else s"${msg.substring(0, maxLength - 3)}...")
      }
    }
    
    
    class Account6 {
      protected var balance = 0.0
    }
    
    
    // 只要有一个具体方法即可
    class SavingsAccount6 extends Account6 with ConsoleLogger6 with ShortLogger6 {
      var interest = 0.0
      def withdraw(amount: Double) {
        if (amount > balance) log("Insufficient funds")
        else balance -= amount
      }
      // More methods ...
    }
    
    
    object Main6 extends App {
      val acct = new SavingsAccount6
      acct.withdraw(100) //Insufficient...
      println(acct.maxLength) //15
    }
     

    11.9 特质中的抽象字段

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

    trait Logger {
      def log(msg: String)
    }
    
    
    trait ConsoleLogger extends Logger {
      def log(msg: String) { println(msg) }
    }
    
    
    trait ShortLogger extends Logger {
      val maxLength: Int // An abstract field
      abstract override def log(msg: String) {
        super.log(
          if (msg.length <= maxLength) msg
          else s"${msg.substring(0, maxLength - 3)}...")
      }
    }
    
    
    class Account {
      protected var balance = 0.0
    }
    
    
    abstract class SavingsAccount extends Account with Logger {
      var interest = 0.0
      def withdraw(amount: Double) {
        if (amount > balance) log("Insufficient funds")
        else balance -= amount
      }
      // More methods ...
    }
    
    
    object Main extends App {
      val acct = new SavingsAccount with ConsoleLogger with ShortLogger {
        val maxLength = 20 // 在这里实现
      }
      acct.withdraw(100)
      // Log message is not truncated because maxLength is 20
    }
     

    11.10 特质构造顺序

    特质也可以有构造器的,构造器中的内容由“字段的初始化” 和一些其他语句构成。
    trait Logger7 {
      println("我在Logger7特质构造器中,嘿嘿嘿。。。")
      def log(msg: String)
    
    }
    
    
    trait ConsoleLogger7 extends Logger7 {
      println("我在ConsoleLogger7特质构造器中,嘿嘿嘿。。。")
      def log(msg: String) {
        println(msg)
      }
    }
    
    
    trait ShortLogger7 extends Logger7 {
      val maxLength: Int
      println("我在ShortLogger7特质构造器中,嘿嘿嘿。。。")
    
      abstract override def log(msg: String) {
        super.log(if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...")
      }
    }
    
    
    class Account7 {
      println("我在Account7构造器中,嘿嘿嘿。。。")
    
      protected var balance = 0.0
    }
    
    
    abstract class SavingsAccount7 extends Account6 with ConsoleLogger7 with ShortLogger7{
      println("我再SavingsAccount7构造器中")
      var interest = 0.0
    
      override val maxLength: Int = 20
    
      def withdraw(amount: Double) {
    
        if (amount > balance) log("余额不足")
        else balance -= amount
      }
    
    }
    
    
    object Main7 extends App {
      val acct = new SavingsAccount7 with ConsoleLogger7 with ShortLogger7
      acct.withdraw(100)
      println(acct.maxLength)
    }

    结果:

    我在Account7构造器中,嘿嘿嘿。。。
    我在Logger7特质构造器中,嘿嘿嘿。。。
    我在ConsoleLogger7特质构造器中,嘿嘿嘿。。。
    我在ShortLogger7特质构造器中,嘿嘿嘿。。。
    我再SavingsAccount7构造器中
    余额不足
    20

    执行顺序:

    1、当前 new 的类的父类 Account7 的构造器执行
    
    2、Logger7 的构造器执行
    
    3、ConsoleLogger7 构造器执行
    
    4、ShortLogger7 构造器执行
    
    5、SavingsAccount7 构造器执行
    
    6、打印

    步骤总结:

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

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

        3、第一个特质构造器

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

        5、第二个特质构造器

        6、当前类构造器

    11.11 初始化特质中的字段

    特质不能有构造器参数,每个特质都有一个无参数的构造器。

    缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有特性,

    比如具体的和抽象的字段,以及超类。现在有如下情景:

    我们想通过特质来实现日志数据的输出,输出到某一个文件中

    import java.io.PrintStream
    
    trait Logger8{
      def log(msg:String)
    
    }
    
    
    trait FileLogger8 extends Logger8{
      val fileName: String
      val out = new PrintStream(fileName)
    
      override def log(msg: String): Unit = {
        out.print(msg)
        out.flush()
      }
    
    }
    
    
    class SavingsAccount8{
    
    }
    
    
    object Main8 extends App {
      val acct = new SavingsAccount8 with FileLogger8 {
        override val fileName = "2019-7-4.log"  //空指针异常
      }
    }



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

    (1)使用“提前定义”

    方式一:

    import java.io.PrintStream
    
    trait Logger8{
      def log(msg:String)
    
    }
    
    
    trait FileLogger8 extends Logger8{
      val fileName: String
      val out = new PrintStream(fileName)
    
      override def log(msg: String): Unit = {
        out.print(msg)
        out.flush()
      }
    
    }
    
    //提前定义一:
    class SavingsAccount8 extends {
      override val fileName = "2017-11-24.log"
    } with FileLogger8
    
    
    object Main8 extends App {
    
      val acct = new SavingsAccount8 with FileLogger8
      acct.log("嘿嘿嘿")
    
    }

    方式二: 

    package unit11
    
    import java.io.PrintStream
    
    trait Logger8{
      def log(msg:String)
    
    }
    
    
    trait FileLogger8 extends Logger8{
      val fileName: String
      val out = new PrintStream(fileName)
    
      override def log(msg: String): Unit = {
        out.print(msg)
        out.flush()
      }
    
    }
    
    
    class SavingsAccount8{
    
    }
    
    
    
    
    object Main8 extends App {//提前定义二:
      val acct1 = new {
        override val fileName = "hello"
      } with SavingsAccount8 with FileLogger8
    
      acct1.log("nihao")
    
    }

    (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("哈哈哈")
    }

    11.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 = "哦,我的上帝,我要踢爆他的屁股!"
     
    }
     

    11.13 自身类型

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

    比如:

    //自身类型特质
     
    trait Logger9{
      this: Exception =>
     
      def log(): Unit ={
        println(getMessage)
      }
    }

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

  • 相关阅读:
    前端知识之JavaScript内容(一)
    前端2css层叠样式表
    前端:html初识以及标签
    css
    python--re模块(正则表达式)
    python--xml模块
    关于导包问题
    前端浅了解
    试写仿优酷系统坑点
    sqlalchemy
  • 原文地址:https://www.cnblogs.com/LXL616/p/11130054.html
Copyright © 2011-2022 走看看