trait

trait

老实说,感觉traitJava中的interface已经很像了,因此下面我们主要说不像的.

  1. Javaimplements来实现接口, scalaextendswith来实现接口

    先看一个经典的有关日志的例子

    object TraitAddExtra {
      def main(args: Array[String]): Unit = {
        val savingsAccount = new SavingsAccount1
        savingsAccount.deposit(1000)
        savingsAccount.withDraw(300)
        savingsAccount.withDraw(800)
      }
    }
    
    trait MyLogger1 {
      def log(msg: String)
    
      def info(msg: String) = log("INFO\t" + msg)
    
      def warn(msg: String) = log("WARN\t" + msg)
    
      def err(msg: String) = log("ERR\t" + msg)
    }
    
    class SavingsAccount1 extends Acount(0, 0) with MyLogger1 {
      def withDraw(amount: Double): Unit = {
        if (amount > balance) err("Insufficient funds")
        else {
          balance -= amount
          info("withDraw money" + amount)
          warn("remained money" + balance)
        }
      }
    
      override def log(msg: String): Unit = println(msg)
    }
    
    class Acount (val id: Int, initialBalance: Double) {
      var balance = initialBalance
    
      def deposit(amount: Double): Double = {
        balance += amount
        balance
      }
    
      override def toString: String = id + "->" + balance
    }
    
    object Acount {
      private var lastNumber = 0
      println("i am in constructor of object")
    
      def newUniqueNumber(): Int = {
        lastNumber += 1
        lastNumber
      }
    
      def apply(initialBalance: Double): Acount = new Acount(newUniqueNumber(), initialBalance)
    
      def main(args: Array[String]): Unit = {
        val accounts = ArrayBuffer[Acount](Acount(100.0), Acount(1000.0), Acount(10000.0))
        accounts.foreach(println)
      }
    }
    
    • 通过以上代码我们发现,scala在这点上和java上没有多大区别,接口里都可以定义抽象方法和具体方法.
  2. trait引入的处理--scala这里采用从最后一个开始倒着处理.(这个说明一下,这种功能java无法实现,无论你用interface还是abstract,均无法调用super.log()这个方法,故无法完成,其实Java里这里采用策略设计模式会去实现,但是也非常不好用,还得靠组合函数去实现.这里就体现出了scala的优势,确实非常舒服!)

    object TraitWhenOverlying extends App {
      //先调用ShortLogger 调用super.log为TimestampLogger调用super.log为PrintLogger
      val act1 = new SavingsAccount with PrintLogger with TimestampLogger with ShortLogger
      //先调用TimestampLogger 调用ShortLogger调用super..log为PrintLogger
      val act2 = new SavingsAccount with PrintLogger with ShortLogger with TimestampLogger
      //如果你想指定调用特定的trait,用super[Trait].log()
      //  val act3 = new SavingsAccount with LongLogger with PrintLogger
      act1.deposit(1000)
      act1.withDraw(400)
      act1.withDraw(700)
      act2.deposit(1000)
      act2.withDraw(400)
      act2.withDraw(700)
      /*act3.deposit(1000)
      act3.withDraw(400)
      act3.withDraw(700)*/
    }
    
    trait TimestampLogger extends PrintLogger {
      override def log(msg: String): Unit = {
        println("我被调用了,我在TimestampLogger里")
        super.log(new Date() + "\t" + msg)
      }
    }
    
    trait ShortLogger extends PrintLogger {
      val maxLength = 15
    
      override def log(msg: String): Unit = {
        println("我被调用了,我在ShortLogger里")
        super.log(if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...")
      }
    }
    

    结果

    我被调用了,我在ShortLogger里
    我被调用了,我在TimestampLogger里
    Tue Jan 09 16:42:16 CST 2018	Insufficient...
    我被调用了,我在TimestampLogger里
    我被调用了,我在ShortLogger里
    Tue Jan 09 1...
    

  3. trait的初始化顺序:注意和上面的super.log比较,会发现方向正好相反,初始化是按照顺序正常构造,super是从右向左来调用,前者的顺序化是线性化的反向,后者的顺序是线性化的方向.

    • 首先调用超类的构造器
    • trait构造器在超类构造器之后,在类构造器之后.
    • trait构造器由左到右被构造
    • 每个trait中,父构造器先被构造
    • 如果多个trait共有一个父trait,而那个父trait已经被构造,则不会被再次构造
    • 所有trait构造完毕,类被构造

    class SavingAcount extends Acount with FileLogger with ShortLogger为例

    • Acount(超类)
    • Logger(第一个trait的父trait)
    • ShortLogger(第一个trait)
    • FileLogger(第二个trait)
    • SavingAcount(类)
  4. trait和类之间的唯一技术差别就是trait不能右构造器,只有一个默认的无参构造器.你应该记得object也是这样,不能有构造器,其他的特性都有.

  5. trait原理

    • trait会被翻译成为JVM的类和接口

    • 抽象方法翻译为接口的抽象方法

    • 具体方法翻译为Java的默认方法

    • 如果有字段:

      • 翻译成接口就有getter,setter方法,其实出国初始化了的化,还会翻译成一个init方法

      • 如果有类继承这个trait,则会此类会获得这个字段,相当于java里的private ...

转载于:https://my.oschina.net/xd03122049/blog/1605327

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值