好记性不如烂笔头


特质

    Scala的trait 和Java 的Interface相比,可以有方法的实现。Scala的Trait支持类和Singleton对象和多个Trait混合(使用来自这些Trait中的 方法,而不时不违反单一继承的原则)。

    Scala为Singleton对象的main定义了一个App trait类型,因此上面的例子可以简化为:

HelloWorld.scalaobject HelloWorld extends App {    println("Hello World!");}

    这段代码就不能作为脚本运行,Scala的脚本要求代码最后以表达式结束。因此运行这段代码,需要先编译这段代码,然后再运行。

// 第一次直接运行,无任何输出    G:\Hadoop\scala-SDK\source>scala HelloWorld.scala// 进行编译G:\Hadoop\scala-SDK\source>scalac HelloWorld.scala// 再次运行G:\Hadoop\scala-SDK\source>scala HelloWorldHello World!

    注意: Scala提供了一个快速编译代码的辅助命令fsc (fast scala compliler) ,使用这个命令,只在第一次使用fsc时启动JVM,之后fsc在后台运行,这样就避免每次使用scalac时都要载入相关库文件,从而提高编译速度。

Scala不支持多重继承,取而代之的是特质,即class week extends month, year是不合法的;

说明:多重继承会产生菱形继承问题
Scala使用特质达到类似多重继承的效果。
特质是Scala里代码复用的基础单元,封装了方法和字段的定义;
特质的定义使用保留字trait,具体语法与类定义类似,除了不能拥有构造参数;

trait reset {    def reset (m : Int, n : Int) = if (m >= n) 1;}

一旦特质被定义了,就可以混入到类中;

class week extends reset {......}

当要混入多个特质时,利用with保留字;

class week extends reset with B with C {......}

特质的成员可以是抽象的,而且不需要使用abstract声明;

同样,重写特质的抽象方法无需给出override;
但是,多个特质重写同一个特质的抽象方法需要给出override;
除了在类定义中混入特质外,还可以:
在特质定义中混入特质

trait reseting extends reset {......}

在对象构造时混入特质

val file = new month with reseting

特质的构造是有顺序的,从左到右被构造,构造顺序为:
    超类
    父特质
    第一个特质
    第二个特质(父特质不重复构造)
    类

简单示例:

/** * @author lucl */class Teacher {  // 实验用的空类  println("===============I'm Teacher.");}  trait TTeacher extends Teacher {  println("===============I'm TTeacher.")  def teach;    // 虚方法,没有实现  }trait TPianoTeacher extends Teacher {  println("===============I'm TPianoTeacher.")  def playPiano = {                // 实方法,已实现    println("I'm playing piano.");  }}class PianoPlayingTeacher extends Teacher with TTeacher with TPianoTeacher {  println("===============I'm PianoPlayingTeacher.")  def teach = {                   // 定义虚方法的实现    println("I'm teaching students.");  }}object TraitOps {  def main (args : Array[String]) {    var p = new PianoPlayingTeacher;    p.teach;    p.playPiano;  }}/**===============I'm Teacher.===============I'm TTeacher.===============I'm TPianoTeacher.===============I'm PianoPlayingTeacher.I'm teaching students.I'm playing piano. */

作为接口的trait代码使用:

我们的例子中定义了一个抽象类Aminal表示所有的动物,然后定义了两个trait Flyable和Swimable分别表示会飞和会游泳两种特征。

Aminmal的实现(定义了walk方法,实现了breathe方法):

package com.mtrait/** * @author lucl * 抽象类 */abstract class Animal {  def walk(speed : Int);    def breathe () = {    println("animal breathes.");  }}

再看下Flyable和Swimable两个 trait的实现:

package com.mtrait/** * @author lucl * 有两个方法,一个抽象方法一个已实现方法 */trait Flyable {  def hasFather = true;  def fly;}package com.mtrait/** * @author lucl * 只有一个抽象方法 */trait Swimable {  def swim;}

我们定义一种动物,它既会飞也会游泳,这种动物是鱼鹰 FishEagle,我们看下代码:

package com.mtrait/** * @author lucl */class FishEagle extends Animal with Flyable with Swimable {  /**   * 实现抽象类的walk方法   */  override def walk(speed: Int) = {    println ("Fish eagle walk with speed : " + speed + ".");  }    /**   * 实现trait Flyable的方法   */  override def fly = {    println("Fish eagle fly fast.");  }    /**   * 实现trait Swimable的方法   */  override def swim {    println("Fish eagle swim fast.");  }}object FishEagle {  def main (args : Array[String]) {    val fish = new FishEagle;    fish.walk(100);    fish.fly;    fish.swim;    println("fish eagle has father ? " + fish.hasFather + ".");    // println(fish.swim);    // 输出为()        println();    val flyable : Flyable = fish;    flyable.fly;       val swimable : Swimable = fish;    swimable.swim;  }}/**输出结果:Fish eagle walk with speed : 100.Fish eagle fly fast.Fish eagle swim fast.fish eagle has father ? true.Fish eagle fly fast.Fish eagle swim fast.*/

    trait很强大,抽象类能做的事情,trait都可以做,它的长处在于可以多继承。

    trait和抽象类的区别在于抽象类是对一个继承链的,类和类之前确实有父子类的继承关系,而trait则如其名字,表示一种特征,可以多继承

在对象中混入trait:

package com.mtrait/** * 单独的日志模块 * 只是标识要记录日志,但没有明确定义如何记录日志 */trait Logger {  def log (msg : String) {}}/** * 记录日志的具体实现类 */trait WriteLogger extends Logger {  override def log (msg : String) = {println("WriteLogger : " + msg);}}/** * 需要执行的业务操作 */trait Action {  def doAction(action : String);}class TraitActionImpl extends Action {  override def doAction(op : String) = println(op);}class LoggerActionImpl extends Action with Logger {  override def doAction(op : String) = {    println(op);     // 如果确实需要日志功能但暂不清楚以何种形式记录日志时,可以采用该方法;    // 当明确了记录日志的方式后,再通过如下在对象中混入trait实现。    log (op);    }}/** * @author lucl */object TraitOps {  def main (args : Array[String]) {    //     println("===================aaaaaa========================");    // 类本身与记录日志Logger没有关系,但是在对象中混入trait的代码后,就具备了日志的功能    val actionA = new TraitActionImpl with WriteLogger;    val op = "业务操作";    actionA.doAction(op);    actionA.log(op);        //    println("===================bbbbbb========================");    // 类实现了Logger,但日志记录是空的操作    val loggerA = new LoggerActionImpl;      loggerA.doAction(op);        println("===================cccccc========================");    // 类实现了Logger,通过在类定义中混入trait实现了自己的记日志的功能    val loggerB = new LoggerActionImpl with WriteLogger;    loggerB.doAction(op);  }}/**输出结果:===================aaaaaa========================业务操作WriteLogger : 业务操作===================bbbbbb========================业务操作===================cccccc========================业务操作WriteLogger : 业务操作*/

比Java的接口方便多了。