trait-多继承详解

0 阅读2分钟

一.多个trait的加载顺序

一个类实现了多个特质之后,所涉及的多个构造器的执行顺序如何确定?

如果有多个父类,则按照从左到右的顺序调用。

package level02

/*
特质: trait
实现多继承,注意观察父子类的构造器的执行顺序
1. 先父后子。
2. 有多个父类,按书写顺序从左向右执行!
 */
object Class024 {
  trait A {
    println("A")
  }

  trait B {
    println("B")
  }

  class Class1 extends A with B {
    println("Class1")
  }

  def main(args: Array[String]): Unit = {
    new Class1()
  }
}

二.多层trait的加载顺序

先执行父类中的构造器,再执行子类的构造器:如果trait1也有自己的父类,要先执行父类构造器

package level02

/*
特质: trait
实现多继承,注意观察父子类的构造器的执行顺序
1. 先父后子。
2. 有多个父类,按书写顺序从左向右执行!
 */
object Class025 {
  trait A {
    println("A")
  }

  trait BB {
    println("BB")
  }
  
  trait B extends BB {
    println("B")
  }

  trait CC {
    println("CC")
  }

  trait C extends CC {
    println("C")
  }

  class Class1 extends C with A with B {
    println("Class1")
  }

  def main(args: Array[String]): Unit = {
    new Class1()
  }
}

三.空指针异常

package level02

import java.io.FileWriter
import scala.util.Using

object Class026 {
  // 在文件中写入日志
  trait FileLogger {
    println(2)
    // 抽象属性:文件名(改为lazy val,确保子类赋值后再初始化)
    val filename: String
    // 延迟初始化FileWriter,避免空指针
    lazy val fileWriter = new FileWriter(filename, true)

    def writeLog(msg: String): Unit = {
      // 使用Using自动管理资源,避免手动close的风险
      Using(fileWriter) { writer =>
        writer.write(msg)
      }
    }
  }

  class MyFileLogger extends FileLogger {
    println(1)
    // 具体的日志文件名
    val filename = "11-28.log"
  }

  def main(args: Array[String]): Unit = {
    val fileLogger = new MyFileLogger()
    fileLogger.writeLog("今天上午上scala课程")
  }
}

方法1:懒加载

lazy val fileout = new PrintWriter(filename)

方法2:提前定义

val p = new {override val filename="p052.log"} with Person051

四.与抽象类的区别

相同点:类和trait都可以定义成员变量(抽象,具体);继承时都使用extends关键字;

不同点:trait的构造器不能带参数;trait支持多继承;