Scala中特质介绍

 

为什么没有多重继承

 

Scala和Java一样不允许从多个超类继承。一开始,这听上去像是个很不幸的局限。为什么类就不能从多个类进行扩展呢?某些编程语言,特别是C++,允许多重继承--但是代价也是出人意料的高。

如果把毫不相干的类组装在一起,多重继承没有什么问题,但是如果这些类具有某些共同字段或方法,麻烦就来了。例如:

class Student {
    private Integer id;
}

class Employee {
    private Integer id;
}


// 假如我们有一个子类继承上面两个父类
class child extend Student,Employee {
    // 此时该类中就继承了两个id, 那么我们在调用该类中id字段的时候根本就不知道是哪一个。
}

 

在Java中应对这种问题采用了非常强的限制策略。类只能扩展自一个超类,它可以实现任意数量的接口,但是接口只能包含抽象方法,不能包含字段。

 

Scala提供特质而非接口。特质可以同时拥有抽象方法和具体方法,而类可以实现多个特质。这个设计解决了Java接口的问题。

 

----

 

当做接口使用的特质

 

下面我们先用Scala定义一个特质

trait Logger{
  // scala中不需要将方法声明为abstract , 特质中未被实现的方法默认就是抽象的
  def log(msg:String) // 抽象方法
}

// 使用 extends 继承特质 ,和Java一样,Scala只能继承一个超类,但可以有任意数量的特质
// 如果需要的特质不止一个我们可以使用 with 关键字来增加额外的特质
class ConsoleLogger extends Logger with Cloneable with Serializable{
  // 重写特质中抽象方法也不需要 override
  def log(msg: String): Unit = println(msg)
}

 

 

带有具体实现的特质

 

在Scala中,特质中的方法并不一定就是抽象的。

trait ConsoleLogger {
  def log(msg: String): Unit = println(msg)
}

// 继承 特质ConsoleLogger 可以直接使用特质中定义的具体方法
class saveAccount extends ConsoleLogger{
  def withdraw() ={
    log("do something")
  }
}

上面例子中 saveAccount继承了特质ConsoleLogger,并且直接调用特质中的log方法,如果使用Java接口是无法实现的。

 

 

带有特质的对象

 

在构造单个对象的时候,你可以为它添加特质。

class SaveAccount extends Account with Logged {

  def withdraw(amount:Double) ={
    log("do something")
  }
}

// 在类中定义一个特质,有一个什么都不做的具体方法,
trait Logged {
  def log(msg:String) = {

  }
}

上面例子中有可能日志不会被记录,但是我们可以在构造具体对象的会后混入一个日志的实现。这样就可以进行日志记录了。

val account = new SaveAccount with Logged
account.log("do something else")

 

 

在特质中重写抽像方法

 

下面这段代码在编译时就会报错,因为我们在Logger中定义的log方法是抽象方法没有具体实现。

trait TimeStampLogger extends Logger {
  // 重写抽象方法
  override def log(msg:String) = {
    // 编译时报错
    /**
     * 报错信息
     * Error:(43, 11) method log in trait Logger is accessed from super.
     * It may not be abstract unless it is overridden by a member declared `abstract' and `override'
     * super.log(new Date() + msg)
     */
    super.log(new Date() + msg)
  }

}

Scala会认为在TimeStampLogger中log()仍然是抽象的,所以需要添加 override 和 abstract 关键字。这样就不会报错。

trait TimeStampLogger extends Logger {
  // 重写抽象方法
  override abstract def log(msg:String) = {
    super.log(new Date() + msg)
  }
}

 

 

特质中的抽象字段、具体字段

 

特质中的字段可以是具体的也可以是抽象的,如果在定义的时候给了初始值那么就是具体的。

trait ShortLogger extends Logged {
  val maxLength = 15 // 具体字段
}

混入该特质的类自动获得一个maxLength字段。通常,对于特质中的每一个具体字段,使用该特质的类都会获得一个字段与之对应。这些字段不是被继承的,它们只是简单地被加到子类当中。

那么为什么会被直接加到子类当中呢。在JVM中,一个类只能扩展一个超类,因此来自特质的字段不能以相同的方式继承,由于这个限制,maxLength字段被直接加到子类中。

 

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

class SaveAccount extends Account with Logged with ShortLogger {

  // 不需要加 override 关键字
  val maxLength: Int = 20
}

 

 

特质构造顺序

 

特质构造器以如下顺序执行

1、首先调用超类的构造器

2、特质构造器在超类构造器调用之后、类构造器调用之前执行

3、特质从左到右被构造

4、每个特质当中,父特质先被构造

5、如果多个特质共有一个父特质,而那个父特质已经被构造,则不会再次被构造

6、所有特质构造完成,子类被构造

 

 

 

长按识别图中二维码

关注获取更多资讯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值