您的位置:首页 > 其它

scala学习笔记11 特质

2016-08-09 23:09 393 查看

本章要点:

一个类扩展自一个或者多个特质,以便使用这些特质提供的服务。特质可能会要求它的类支持某个特定的特性,和java接口不同,scala特质可以给出这些特性的缺省实现。

类可以实现任意数量的特质
特质可以要求实现他们的类具有特定的字段,方法或者超类
和java接口不一样,scala特质可以提供方法和字段的实现
当你将多个特质叠加在一起时,顺序很重要---方法先被执行的特质排在后面
1、为什么没有多重继承?

scala和java一样,不允许类从多个超类继承,因为如果多重继承的类具有某些共通的方法或字段,就会出现无法辨明使用哪个方法或者字段。

接下来,假设student和employee都继承自同一个超类person

class Person {

var name:String= _

}

class student extends Person{}

class Employee extends Person{}

这就引入了菱形继承问题。在teachassistant中我们只想一个name字段,而不是两个。(java的类可以实现任意的数量的借口,但借口只能包含抽象方法,不能包含字段)




特质可以同时具有抽象方法和具体方法,而类可以实现多个特质。

2、当作接口使用的特质

trait Logger {

def log(msg:String) //抽象方法,特质中未被实现的方法默认是抽象的

}

class  ConsoleLogger extends Logger {

def log(msg:String) {println(msg)} //不需要override

}

比起java的借口,特质和类更为相似。

如果你需要的特质不止一个,可以用with关键字来添加额外的特质:

class  ConsoleLogger extends Logger with Cloneable with Serializable

这里使用java类库的两种接口,所有的java接口都可以当成scala特质使用。

总结:scala类只能有一个超类但是可以有任意数量的特质。

3、带有具体实现的特质

trait ConsoleLogger{

 def log(msg:String) {}

}

以下是如何使用这个特质的示例

savingaccount从consolelogger特质得道一个log方法,java接口不能做到。

4、带有特质的对象

trait Logged {
def log(msg:String){}
}



上面的类看上去毫无意义,但是在构造具体对象时可以混入一个更好的日志记录器的实现。

trait ConsoleLogger extends logged{

   override def log(msg:String) {}



可以在构造对象时加入特质:

val acct=new savingaccount with consolelogger

还可以在另一个对象上加入不同的特质

val acct2=new savingaccount with filelog

5、叠加在一起的特质
  你可以为类或者对象添加多个互相调用的特质,从最后一个开始。
  首先,加时间戳


注意上述的log方法将每一个修改过的消息传递给super.log。
对于特质而言,super.log并不想类那样拥有相同的含义。(实际上,logged中的log函数毫无用处)实际上,super.log调用的是特质层级下一个特质。
可以利用super[consolelogger].log(...)规定哪一个特质的方法被调用。
这篇文章里的例子很好:http://blog.csdn.net/o1101574955/article/details/51815018

6、在特质中重写抽象方法
先引入一个没有被实现的logger特质
trait Logger {
  def  log(msg:String) }
现在使用时间戳特质来扩展它
trait TimestampLogger extends Logger {
 override def log(msg:String){
 super.log(new java.util.Date()+" "+msg) }
}
但是在编译时会将super.log标记为错误。因为根据正常的继承规则,Logger.log没有被实现,我们无法知道哪个log最终被调用。scala认为TimestampLogger仍旧是抽象的,它需要混入一个具体的log方法,所以需要在方法上打上abstract关键字和override关键字。
7、当作富接口使用的特质
特质可以包含大量工具方法,而这些工具方法可以依赖一些抽象方法来实现,例如scala的Iterator特质就利用抽象的next和hasNext定义了几十个方法。
trait Logger {
 def log(msg:String)
 def info(msg:String) { log("INFO:"+msg)}
 def warn(msg:String) { log("WARN:"+msg) } 
 def severe(msg:String) {  log("SEVERE:"+msg) }
}
这样一来,我们可以使用logger特质任意调用这些log的方法:



8、特质中的具体字段
特质中的字段可以是具体的,也可以是抽象的,如果你给出了初始值,那么字段就是具体的。
trait ShortLogger extends Logged {
val maxLength =15 }
混入该特质的类自动获得一个maxlebgth字段。通常,对于特质中的每个字段,使用该特质的类都会获得一个字段与之对应,这些字段不是被继承的,只是简单的被加到子类中。
class SavingAccount extends Account  with ConsoleLogger with ShortLogger {
var intesest=0.0
...
}
class Account {
var balance=0.0
}
savingaccount类以正常的方式继承了balance字段。但是对于maxlength字段,则不一样。



9、特质中的抽象字段

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

如下maxLength字段是抽象的:
trait ShortLogger extends Logged  {
  val maxLength:Int 
  override def log(msg:String) {
     super.log(if (msg.length<=maxLength))  msg  else mag.substring(0,maxLength-3)+"...")
}
...}
当你在一个具体的类中使用该特质时,必须提供maxlength字段。
class SavingAccount
ba61
extends Account with ConsoleLogger with ShortLogger {
 val maxLength=20  //不需要override
...}

这种提供特质参数的方式十分的方便,比如:

val acct=new SavingAccount with ConsoleLogger with ShortLogger {
  val maxLength=20
}  //注意下文11,这种方法似乎存在问题??
10、特质构造顺序
和类一样,特质也可以有构造器,由字段的初始化和其它特质体中的语句构成。例如:
trait FileLogger extends Logger {
  val  out=new PrintWriter("app.log")
  out.println("#"+new Date().toString)   //这是特质构造器的一部分
  def log(msg:String) { out.println(msg) ; out.flush() } //这是特质构造器的一部分
}

这些语句在任何混入该特质的对象在构造时都会被执行。
构造器以如下顺序执行:


举例来说,考虑如下一个类的构造顺序
class SavingAccount extends Account with FileLogger with ShortLogger



11、初始化特质中的字段
特质不能有构造器参数,每个特质都有一个无参数的构造器。
注意点:缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有特性,比如具体的和抽象的字段以及超类。
val acct=new SavingAccount with FileLogger("myapp.log")
//错误:特质不能够使用构造器参数
trait FileLogger extends Logger {
 val filename:String
val out=new printwriter(filename)
....}
使用该特质的类可以重写filename字段,不过下面的方案并不可行:
val  acct=new SavingAccount with FileLogger {
 val filename = "myapp.log"  //错误
}



12、扩展类的特质
trait a extends b {}
class c extends a{}
如上,假设一个特质a继承一个类b,类c继承a,则b自动成为c的超类。但是如果c还extends另外一个类,怎么办?
只要这两个类是b的子类就行。
13、背后发生了什么?
scala需要将特质翻译成jvm的接口和类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scala