鲁春利的工作笔记好记性不如烂笔头



特质

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

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

HelloWorld.scala
object 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 HelloWorld
Hello 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的接口方便多了j_0057.gif。