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

    一、为什么没有多重继承

    c++允许多重继承

    Java不允许多重继承,类只能继承一个超类,可以实现任意数量的接口。

    如何继承这两个抽象基类?

    Scala提供“特质”而非接口;特质可以同时抽象方法和具体方法。类可以实现多个特质。

    二、当做接口使用的特质

      trait Logger{
        def log(msg: String)
      }
    
      class ConsoleLogger extends Logger{   //使用 extends 不能使用 implements
    
        def log(msg: String): Unit ={     // 不需要使用override
          println(msg)
        }
      }

    三、带有具体实现的特质

      trait Logger{
        def log(msg: String){println(msg)}
      }
    
      class SavingAccount extends Logger{
        def display(msg: String): Unit ={
          log(msg)
        }
      }
    
      val v = new SavingAccount()
      v.display("Hello")

    :特质发生变化,所有混入特质的类都需要重新编译。

    四、带有特质的对象

      trait Logged{
        def log(msg: String){}     //特质,方法是个空实现
      }
    
      trait ConsoleLogger extends Logged{
        override def log(msg: String) {println(msg)}
      }
    
      class SavingAccount extends Logged{  //类继承特质,方法的实现结果为空
        def display(msg:String){log(msg)
        }
      }
    
      val s = new SavingAccount()        //构造对象,结果为空
      s.display("Hello")
    
      val s1 = new SavingAccount() with ConsoleLogger //在构造对象的时候,加入特质,结果为 Hello
      s1.display("Hello")

    五、叠加在一起的特质

    trait Logged{
        def log(msg: String){}
      }
    
      trait ConsoleLogger extends Logged{
        override def log(msg: String) {println(msg)}
      }
    
      trait TimestampLogger extends Logged{
        override def log(msg: String){
          super.log(new java.util.Date() + " " + msg)
        }
      }
    
      trait ShortLogger extends Logged{
        val maxLength = 15
        override def log(msg: String){
          super.log(
            if(msg.length <= maxLength) msg else msg.substring(0, maxLength-3) + "..."
          )
        }
      }
    
      class SavingAccount extends Logged {
        def display(msg: String) {
          log(msg)
        }
      }
    
      val s = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger //ShortLogger的log方法先执行,然后是 TimestampLogger
      s.display("Hello")
    
      val s1 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger //TimeStampLogger的方法先执行,然后是 ShortLogger
      s1.display("Hello")

    结果:

    Wed Aug 31 23:27:53 CST 2016 Hello
    Wed Aug 31 2...

    六、在特质中重写抽象方法

        trait Logged{
          def log(msg: String)
        }
    
        trait ConsoleLogger extends Logged{
          override def log(msg: String) {println(msg)}
        }
        trait TimestampLogger extends Logged{
          abstract override def log(msg: String){   //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
            super.log(new java.util.Date() + " " + msg)
          }
        }
    
        class SavingAccount extends ConsoleLogger {
          def display(msg: String) {
            log(msg)
          }
        }
    
        val s = new SavingAccount with TimestampLogger
        s.display("Hello")

    结果:

    Wed Aug 31 23:51:40 CST 2016 Hello

    七、当做富接口使用的特质

        trait Logged{                           // 特质将抽象方法,和具体方法结合在一起
          def log(msg: String)
          def info(msg: String){log(msg)}
          def warn(msg: String){log(msg)}
        }
    
        trait ConsoleLogger extends Logged{
          override def log(msg: String) {println(msg)}
        }
        trait TimestampLogger extends Logged{
          abstract override def log(msg: String){   //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
            super.log(new java.util.Date() + " " + msg)
          }
        }
    
        class SavingAccount extends ConsoleLogger {
          def display(msg: String) {
            info(msg)
          }
        }
    
        val s = new SavingAccount with TimestampLogger
        s.display("Hello")

    结果:

    Thu Sep 01 09:38:54 CST 2016 Hello

    八、特质中的具体字段

    在子类中添加特质,特质中的字段就相当于子类中的字段

    九、特质中的抽象字段

     特质中的抽象字段,在子类的具体实现中必须要被重写

        trait Logged{                           // 特质将抽象方法,和具体方法结合在一起
          val maxLength: Int                    // 抽象字段
          def log(msg: String)
          def info(msg: String){log(msg)}
          def warn(msg: String){log(msg)}
        }
    
        trait ConsoleLogger extends Logged{
          override def log(msg: String) {println(msg)}
        }
        trait TimestampLogger extends Logged{
          abstract override def log(msg: String){   //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
            super.log(new java.util.Date() + " " + msg)
          }
        }
    
        class SavingAccount(val maxLength: Int) extends ConsoleLogger { //抽象字段当做参数传递
          //val maxLength = 20        //抽象字段在类中被重写
          def display(msg: String) {
            info(msg)
            info(maxLength.toString)
          }
        }
    
        val s = new SavingAccount(20)  with TimestampLogger //传递参数
        s.display("Hello")

    结果:

    Thu Sep 01 10:05:55 CST 2016 Hello
    Thu Sep 01 10:05:55 CST 2016 20

    十、特质构造顺序

    1、首先调用超类的构造器

    2、然后调用特质构造器,特质构造器在超类构造器之后,类构造器之前

    3、特质由左到右被构造

    4、在每个特质当中,父特质先被构造

    5、如果多个特质公用一个父特质,而那个父特质已经被构造过了,则不会再被构造

    6、所有特质构造完毕,子类被构造

    例子:

    class SavingAccount extends Account with FileLogger with ShortLogger

    构造器执行顺序:

    1、Account(超类)

    2、Logger(第一个特质的父特质)

    3、FileLogger(第一个特质)

    4、ShortLogger(第二个特质)

    5、SavingAccount(类)

    特质方法Super被解析的顺序 从右向左

    ShortLogger(super)->FileLogger(super)->Logger

      trait Logged{
        def log(msg: String){}
      }
    
      trait ConsoleLogger extends Logged{
        override def log(msg: String) {println(msg)}
      }
    
      trait TimestampLogger extends Logged{
        override def log(msg: String){
          println("This is TimestampLogger")
          super.log(new java.util.Date() + " " + msg)
        }
      }
    
      trait ShortLogger extends Logged{
        val maxLength = 15
        override def log(msg: String){
          println("This is ShortLogger")
          super.log(
            if(msg.length <= maxLength) msg else msg.substring(0, maxLength-3) + "..."
          )
        }
      }
    
      class SavingAccount extends Logged {
        def display(msg: String) {
          log(msg)
        }
      }
    
      val s = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger
      s.display("Hello")
    
      val s1 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger
      s1.display("Hello")

    结果:

    This is ShortLogger
    This is TimestampLogger
    Thu Sep 01 13:55:33 CST 2016 Hello
    This is TimestampLogger
    This is ShortLogger
    Thu Sep 01 1...

    十一、初始化特质中的字段

    特质中不能使用构造参数

    要想初始化特质中的字段,可使用如下方式:

    1、提前定义,在特质的构造函数之前定义

      trait Logger{
        def log(msg: String){}
      }
    
      trait FileLogger extends Logger{
        val filename: String
        val out = new PrintWriter(filename)
        override def log(msg: String) {
          out.println(msg)
          out.flush()
        }
      }
    
      class SavingAccount extends Logger{
        def display(msg: String): Unit ={
          log(msg)
        }
      }//val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
      val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
    
      c.display("hello11")

    2、使用lazy值

      trait Logger{
        def log(msg: String){}
      }
    
      trait FileLogger extends Logger{
        val filename: String
        lazy val out = new PrintWriter(filename)  //使用懒值 out 在使用时,才会初始化,那时filename值已经被正确设置了
        override def log(msg: String) {
          out.println(msg)
          out.flush()
        }
      }
    
      class SavingAccount extends Logger{
        def display(msg: String): Unit ={
          log(msg)
        }
      }//val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
      //val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
      val c = new SavingAccount with FileLogger { val filename="mylog.txt" }
    
      c.display("hello world")

    懒值在每次使用前都会检查是否已经初始化,用起来并不是那么高效

    3、使用类主构造器传参

      trait Logger{
        def log(msg: String){}
      }
    
      trait FileLogger extends Logger{
        val filename: String
        //lazy val out = new PrintWriter(filename)  //使用懒值 out 在使用时,才会初始化,那时filename值已经被正确设置了
        val out = new PrintWriter(filename)
        override def log(msg: String) {
          out.println(msg)
          out.flush()
        }
      }
    
    //  class SavingAccount extends Logger{
    //    def display(msg: String): Unit ={
    //      log(msg)
    //    }
    //  }
    
      class SavingAccount(val filename: String) extends Logger{
        def display(msg: String): Unit ={
          log(msg)
        }
      }
    
      //val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
      //val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
      //val c = new SavingAccount with FileLogger { val filename="mylog.txt" }
      val c = new SavingAccount("mylog.txt") with FileLogger
    
      c.display("hello world 111")

    十二、扩展类的特质

    特质可以扩展另一个特质,特质也可以扩展类

    类扩展了特质,特质扩展了类,特质的超类成为我们类的超类;

    如果类扩展了另一个类,只要那个类是特质超类的子类就可以。

    如果扩展多个类,且是不相干的,类不能有多个超类

      trait Logged{
        def log(msg: String){}
      }
    
      trait LoggedException extends Exception with Logged{ //特质扩展超类
          def log() {log(getMessage())}
      }
    
      class UnhappyException extends LoggedException{  //类扩展特质,特质的超类Exception 也成为了类UnHappyException的超类
        override def getMessage() = "aaa"
      }
    
      class UnhappyException extends IOException with LoggedException //可以扩展 IOException是 Exception的子类
    
      class UnhappyException extends JFrame with LoggedException // JFrame 和 Exception 没继承关系 不能扩展

    十三、自身类型

    this : Exception =>

        trait Logged{
          def log(msg: String){}
        }
    
        trait LoggedException extends Logged{ //特质扩展超类
            this: Exception =>             //自身类型  自身类型为Exception,它只能被混入Exception的子类
            def log() {log(getMessage())}
        }
    
        val f = new JFrame with LoggedException //错误 JFrame不是 Exception的子类型,而Exception是LoggedException的自身类型

    结构类型:

      trait Logged{
        def log(msg: String){}
      }
    
      trait LoggedException extends Logged{   //特质扩展超类
        this: {def getMessage():String} =>     //自身类型(结构类型) 这个特质可以被混入任何拥有getMessage方法的类
        def log() {log(getMessage())}
      }

    十四、背后发生了什么

    参考《快学Scala》

  • 相关阅读:
    Java中HashMap底层实现原理(JDK1.8)源码分析
    java io系列01之 "目录"
    数据结构与算法系列 目录
    Java 集合系列目录(Category)
    ls参数
    在PATH路径中添加新的路径
    目录相关的操作
    chmod
    属性类型
    ls -al
  • 原文地址:https://www.cnblogs.com/one--way/p/5830903.html
Copyright © 2011-2022 走看看