Trait详解

基础知识

  1. Trait(特征):是一种特殊的概念,相当于 Java 的接口,实际上它比接口还功能强大,它还可以定义属性和方法的实现

  2. Trait的定义:定义的方式和定义类的方式相同
  3. scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可,解决类的单继承问题

  4. 类继承trait后,必须实现其中的抽象方法和抽象字段

  5. trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类

抽象方法和抽象字段

  1. 在triat中可以定义抽象方法,定义的抽象方法可以直接在trait中引用
  2. Triat可以定义抽象field,定义的抽象字段可以在trait中直接引用,此时抽象字段有默认值
object test11 {
  trait Sayhello{
    val name :String  //抽象字段
    println(name)  //调用抽象字段
    def sayhello(name:String)=println(s"hello,$name")  //具体方法调用抽象字段
  }
  trait MakeFriends{
    def makeFrinds(str:String)  //抽象方法
    def log(msg: String) {} //{}表示不是抽象方法
  }
  class Person(val name:String) extends Sayhello with MakeFriends{  //with实现多继承
    def makeFrinds(str:String)=print(s"可以交个朋友吗?$str")   //实现MakeFriends中的抽象方法
  }
  def main(args: Array[String]): Unit = {
    val person=new Person("Wei")
    person.sayhello("Tom")
    person.makeFrinds("Tom")
  }
}

 

具体方法和具体字段

  1. 就像普通类中的具体方法和具体字段一样
object test12{
      trait Person {
        val age: Int = 2
        def eat(food: String) = println(s"吃 $food")
      }
      class Student(val name: String) extends Person {}

  def main(args: Array[String]): Unit = {
    val person = new Student("tom")
    println(person.age)
    person.eat("橘子")
  }
}

 

高级知识

实例对象继承trait

  1. 创建对象的时候只需要某个实例对象继承trait,而不需要类中的其他对象继承trait
  2. 对象动态继承的trait会覆盖对象实现类的成员(Man覆盖了Person方法)
object  test21{
  trait Person {
    def eat(food: String) {println("不喜欢吃"+food)}
  }
  trait Man extends Person {
    override def eat(food: String){println("喜欢吃"+food)}
  }

  class Teacher(val name: String) extends Person {
    def sayHello {
      println("你好,我是" + name)
      eat("水果") }
  }
  def main(args: Array[String]): Unit = {
    val p1 = new Teacher("wiki")
    p1.sayHello
    println("\t")
    val p2 = new Teacher("jack") with Man
    p2.sayHello
  }
}

 

调用链

  1. 类继承traitA,traitA继承traitB,traitB继承traitC,traitC继承traitD,如果要同时调用trait[A-D]中相同的方法fun1,只要将只需要将super.fun1放入到fun1中即可,形成一个调用链条
object  test22 {
  trait KeyWord {
    def logger(data: String): Unit = {
      println("第一次密码是:" + data)
    }
  }
  trait KeyWord1 extends KeyWord {
    override def logger(data: String): Unit = {
      println("第二次密码是:" + data)
      super.logger(data)
    }
  }
  trait KeyWord2 extends KeyWord1 {
    override def logger(data: String): Unit = {
      println("第三次密码是:" + data)
      super.logger(data)
    }
  }
  class KeyWord3 extends KeyWord2 {
    def log(data: String): Unit = {
      logger(data)
    }
  }
  def main(args: Array[String]): Unit = {
    val s = new KeyWord3
    s.log("car360")
  }
}
/**
  * 输出结果:
  * 第三次密码是:car360
  * 第二次密码是:car360
  * 第一次密码是:car360
  */

 

abstract override

  1. trait可以直接引用抽象方法,但是子类中不可以直接引用父类的抽象方法,需要引用必须用abstract override修饰
object  test23{
  trait Person {
    def play(str: String)
    def eat(str: String) { play(str) }
  }
  trait Teacher extends Person {
    abstract override def play(str: String) { super.play(str) }
    def eat(str: String) { super.eat(str) }   //会报错
        def say(str: String) { super.play(str) }
  }
}

 

构造机制

  1. trait也有构造函数
  2. 继承了trait的类的构造机制如下:
    1. 父类的构造函数先执行
    2. 类继承的traitA如果有父traitB,父traitB的构造代码先执行(多个trait继承同一个父trait,则父trait只会构造一次)
    3. trait的构造代码执行,多个trait从左到右依次执行
    4. 子类的构造函数执行
object  test24{
  class Person { println("Person's constructor!") }
  trait Number { println("Number's constructor!") }
  trait First extends Number { println("First's constructor!") }
  trait Second extends Number { println("Second's constructor!") }
  class Student extends Person with First with Second {
    println("Student's constructor!")
  }
  def main(args: Array[String]): Unit = {
    val stu = new Student
  }
}
//运行结果:
//Person's constructor!
//Number's constructor!
//First's constructor!
//Second's constructor!
//Student's constructor!

 

 

字段的初始化

 

  1.  trait没有接收参数的构造函数
  2. trait中的抽象字段都有默认值
  3. 提前定义:在调用trait的构造函数时候重写了字段值
  4. lazy:不能用于抽象字段,只能用于具体字段

方法一:提前定义

object test25 {
  trait Person{
    val num:String
    println(num)  
    println(num.toInt+123)
  }
  class Student(number: String) extends Person{
    val num:String=number
    print("the new num is "+num)
  }
  def main(args: Array[String]): Unit = {
//    val s= new Person("123") //会报错因为他会先执行trait的println num此时是null,这时就需要用到提前定义了
    val p = new {
      override val num: String = "123"
    } with Student("456")           //注意:传入的参数是123而不是456
  }
}

 

方法二:用lazy

trait Person{
  lazy  val num:String=null //注意lazy不能用于抽象字段,只能用与具体字段
  println(num.toInt+123)
}
class Student (number: String) extends Person{
  override lazy val num:String=number
  print("the new num is "+num)
}
object test {
  def main(args: Array[String]): Unit = {
    val p = new Student("123")
  }
}

 

 



posted @ 2019-10-15 15:18  WeiKing  阅读(2222)  评论(0编辑  收藏  举报