scala特质

所有的面向对象的语言都不允许直接的多重继承,因为会出现“deadlydiamond of death”问题。Scala提供了特质(trait),特质可以同时拥有抽象方法和具体方法,一个类可以实现多个特质。

当作接口使用的特质

特质中没有实现的方法就是抽象方法。类通过extends继承特质,通过with可以继承多个特质。

trait Logger {
  def log(msg: String)
}
class ConsoleLogger extends Logger with Cloneable with Serializable {
  def log(msg: String) {
    println(msg)
  }
}

Logger with Cloneable with Serializable是一个整体,extends这个整体

所有的java接口都可以当做Scala特质使用。

带有具体实现的特质

特质中的方法并不一定是抽象的:

trait ConsoleLogger {
  def log(msg: String) {
    println(msg)
  }
}
class Account {
  protected var balance = 0.0
}
class SavingsAccount extends Account with ConsoleLogger {
  def withdraw(amount: Double) {
    if (amount > balance) log("余额不足")
    else balance -= amount
  }
}

带有特质的对象,动态混入

在构建对象时混入某个具体的特质,覆盖掉抽象方法,提供具体实现

trait Logger {
  def log(msg: String)
}
trait ConsoleLogger extends Logger {
  def log(msg: String) {
    println(msg)
  }
}
class Account {
  protected var balance = 0.0
}
abstract class SavingsAccount extends Account with Logger {
  def withdraw(amount: Double) {
    if (amount > balance) log("余额不足")
    else balance -= amount
  }
}
object Main extends App {
  val account = new SavingsAccount with ConsoleLogger
  account.withdraw(100)
}

叠加在一起的特质

super并不是指继承关系,而是指的加载顺序。继承多个相同父特质的类,会从右到左依次调用特质的方法。Super指的是继承特质左边的特质,从源码是无法判断super.method会执行哪里的方法,如果想要调用具体特质的方法,可以指定:super[ConsoleLogger].log().其中的泛型必须是该特质的直接超类类型(执行顺序从右向左)

trait Logger {
  def log(msg: String);
}
trait ConsoleLogger extends Logger {
  def log(msg: String) {
    println(msg)
  }
}
trait TimestampLogger extends ConsoleLogger {
  override def log(msg: String) {
    super.log(new java.util.Date() + " " + msg)
  }
}
trait ShortLogger extends ConsoleLogger {
  override def log(msg: String) {
    super.log(if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...")
  }
}
class Account {
  protected var balance = 0.0
}
abstract class SavingsAccount extends Account with Logger {
  def withdraw(amount: Double) {
    if (amount > balance) log("余额不足")
    else balance -= amount
  }
}
object Main extends App {
  val acct1 = new SavingsAccount with TimestampLogger with ShortLogger
  val acct2 = new SavingsAccount with ShortLogger with TimestampLogger
  acct1.withdraw(100)
  acct2.withdraw(100)
}

在特质中重写抽象方法

trait Logger2 {
  def log(msg: String)
}
//因为有super,Scala认为log还是一个抽象方法
trait TimestampLogger2 extends Logger2 {
  abstract override def log(msg: String) {
    super.log(new java.util.Date() + " " + msg)
  }
}
trait ShortLogger2 extends Logger2 {
  abstract override def log(msg: String) {
    super.log(if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...")
  }
}
trait ConsoleLogger2 extends Logger2 {
  override def log(msg: String) {
    println(msg)
  }
}
class Account2 {
  protected var balance = 0.0
}
abstract class SavingsAccount2 extends Account2 with Logger2 {
  def withdraw(amount: Double) {
    if (amount > balance) log("余额不足")
    else balance -= amount
  }
}
object Main2 extends App {
  //这里可以根据上面的知识点理解此处
  val acct1 = new SavingsAccount2 with ConsoleLogger2 with TimestampLogger2 with ShortLogger2
  acct1.withdraw(100)
}

特质中的具体字段

特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。混入该特质的类就具有了该字段,字段不是继承,而是简单的加入类。是自己的字段。

特质中的抽象字段

特质中未被初始化的字段在具体的子类中必须被重写

特质构造顺序

在特质中如果有定义变量,那么一定在使用的时候提前定义,不然会出现空的情况

特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成

1、调用当前类的超类构造器

2、第一个特质的父特质构造器

3、第一个特质构造器

4、第二个特质构造器的父特质构造器由于已经执行完成,所以不再执行

5、第二个特质构造器

6、当前类构造器

特质不能有构造器参数,每个特质都有一个无参数的构造器。缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有特性,比如具体的和抽象的字段,以及超类。

1、特质可以继承自类,以用来拓展该类的一些功能

2、所有混入该特质的类,会自动成为那个特质所继承的超类的子类

3、如果混入该特质的类,已经继承了另一个类,不就矛盾了?注意,只要继承的那个类是特质超类的子类即可。

自身类型

主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。比如:

//自身类型特质
trait Logger9{
  this: Exception =>
  def log(): Unit ={
    println(getMessage)
  }
}

你可能感兴趣的:(scala)