trait:多继承详解

44 阅读3分钟

(一)trait多继承构造器的执行顺序

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

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

package level02

/*
 特质
 trait:实现多继承
 trait多继承构造器的执行顺序
 1.先父  后子
 2.如果是多继承,有多个trait,按书写顺序从左到右
* */
object Q01 {
  trait A {
    println("A 特质构造器")
  }

  trait B {
    println("B 特质构造器")
  }

  trait C {
    println("C 特质构造器")
  }

  class Child() extends C with B with A {
    println("child ...")
  } // 继承三个特质A,B,C

  def main(args: Array[String]): Unit = {
    val child = new Child()
  }
}

(二)多层trait的加载顺序

先执行父类中的构造器,再执行子类的构造器:

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

/*
 特质
 trait:实现多继承
 trait多继承构造器的执行顺序
 1.先父  后子
 2.如果是多继承,有多个trait,按书写顺序从左到右
* */
object Q02 {

  trait AA {
    println("A 特质构造器")
  }
  trait A extends AA {
    println("A    构造器")
  }

  trait B {
    println("B 特质构造器")
  }

  trait CC {
    println("CC 特质构造器")
  }

  trait C extends CC {
    println("C     构造器")
  }

  class Child() extends C with B with A {
    println("child ...")
  } // 继承三个特质A,B,C

  def main(args: Array[String]): Unit = {
    val child = new Child()
  }
}

(三)空指针异常

执行错误代码

package level02

import java.io.FileWriter
object Q03 {

  trait FileLogger {
    val filename: String
    val writer = new FileWriter(filename)

    def writeLog(msg: String): Unit = {
      writer.write(msg)
      writer.close()
    }
  }

  class MyWriter extends FileLogger {
    override val filename: String = "test.log"
  } // 继承三个特质A,B,C

  def main(args: Array[String]): Unit = {
    val log = new MyWriter()
    log.writeLog("测试内容")
  }
}

调用p.log()时,fileName没有值。这就是继承时带的问题:先执行了trait构造器的代码,后执行了具体子类的构造器。而具体的赋值操作是在子类的构造器中才进行,所以,父类的filename没有值,导致空指针异常。

问题解决

方法1:懒加载

使用 lazy val 延迟 writer 的初始化

ackage level02
import java.io.FileWriter
/*
*/
object Q03 {

  trait FileLogger {

    println("filelogger")

    val filename: String

    // 懒加载效果
    // 这个对象不会立刻创建,而是等到你需要使用的时候才去创建

    lazy val writer = new FileWriter(filename)

    def writeLog(msg: String): Unit = {
      writer.write(msg)
      writer.close()
    }
  }

  class MyWriter extends FileLogger {
    println("MyWriter")
    override val filename: String = "test.log"
  } // 继承三个特质A,B,C

  def main(args: Array[String]): Unit = {
    val log = new MyWriter()
    log.writeLog("测试内容")
  }
}

(四)trait和类的区别

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

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

package level02
import java.io.FileWriter

/*
  trait 和 class 的区别
  1. class 类: 付生类,抽象类,内部类,不能多继承。
  2. trait 特质,可以多继承。构造器不能带参数。 extends  with


  共同点
  1. 都可以有:具体属性,抽象属性,具体方法,抽象方法
  2. 都能用extends来继承
*/
object Q05 {

  class A{}
  trait B{}
  class AB extends A with B{

  }

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

  }
}