,好记性不如烂笔头
特质
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的接口方便多了。