Scala入门2(特质与叠加在一起的特质)

一、介绍

  参考http://luchunli.blog.51cto.com/2368057/1705025

  我们知道,如果几个类有某些共通的方法或者字段,那么从它们多重继承时,就会出现麻烦。所以Java被设计成 不支持多重继承,但可以实现任意多的接口。接口只能包含抽象方法,不能包含字段。

  特质 trait 是Scala中特有的一项特点,不同于C#与Java,如果一定要拿C#与Java中的某项特点作对比的话,最接近的应该是接口,但是C#与Java中的接口是不允许带有方法实现的,而Scala中的Trait是可以带有方法实现的。这样做的好处需要某个trait就拿来用,而不需要重复实现接口

  所有的Java接口都可以作为Scala特质来使用。与Java一样,Scala类只能有一个超类,可以有任意数量的特质。

  特质的定义使用保留字trait,具体语法与类定义类似,除了不能拥有构造参数

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

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

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

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

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

  特质的成员可以是抽象的,而且不需要使用abstract声明。同样,重写特质的抽象方法无需给出override。但是,多个特质重写同一个特质的抽象方法需要给出override。
  除了在类定义中混入特质外,还可以在特质定义中混入特质

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

  在对象构造时混入特质

1 val file = new month with reseting

  特质的构造是有顺序的,从左到右被构造

 1 /**
 2  * @author lucl
 3  */
 4 class Teacher {  // 实验用的空类
 5   println("===============I'm Teacher.");
 6 }  
 7  
 8 trait TTeacher extends Teacher {
 9   println("===============I'm TTeacher.")
10   def teach;    // 虚方法,没有实现  
11 }
12  
13 trait TPianoTeacher extends Teacher {
14   println("===============I'm TPianoTeacher.")
15   def playPiano = {                // 实方法,已实现
16     println("I'm playing piano.");
17   }
18 }
19  
20 class PianoPlayingTeacher extends Teacher with TTeacher with TPianoTeacher {
21   println("===============I'm PianoPlayingTeacher.")
22   def teach = {                   // 定义虚方法的实现
23     println("I'm teaching students.");
24   }
25 }
26  
27 object TraitOps {
28   def main (args : Array[String]) {
29     var p = new PianoPlayingTeacher;
30     p.teach;
31     p.playPiano;
32   }
33 }
34  
35 /**
36 ===============I'm Teacher.
37 ===============I'm TTeacher.
38 ===============I'm TPianoTeacher.
39 ===============I'm PianoPlayingTeacher.
40 I'm teaching students.
41 I'm playing piano.
42  */

二、例子

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

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

1 abstract class Animal {
2   def walk(speed : Int);
3    
4   def breathe () = {
5     println("animal breathes.");
6   }
7 }

  Flyable和Swimable两个 trait的实现

 1 /**
 2  * @author lucl
 3  * 有两个方法,一个抽象方法一个已实现方法
 4  */
 5 trait Flyable {
 6   def hasFather = true;
 7   def fly;
 8 }
 9  
10 package com.mtrait
11  
12 /**
13  * @author lucl
14  * 只有一个抽象方法
15  */
16 trait Swimable {
17   def swim;
18 }

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

 1 /**
 2  * @author lucl
 3  */
 4 class FishEagle extends Animal with Flyable with Swimable {
 5   /**
 6    * 实现抽象类的walk方法
 7    */
 8   override def walk(speed: Int) = {
 9     println ("Fish eagle walk with speed : " + speed + ".");
10   }
11    
12   /**
13    * 实现trait Flyable的方法
14    */
15   override def fly = {
16     println("Fish eagle fly fast.");
17   }
18    
19   /**
20    * 实现trait Swimable的方法
21    */
22   override def swim {
23     println("Fish eagle swim fast.");
24   }
25 }
26  
27 object FishEagle {
28   def main (args : Array[String]) {
29     val fish = new FishEagle;
30     fish.walk(100);
31     fish.fly;
32     fish.swim;
33     println("fish eagle has father ? " + fish.hasFather + ".");
34     // println(fish.swim);    // 输出为()
35      
36     println();
37     val flyable : Flyable = fish;
38     flyable.fly;
39     
40     val swimable : Swimable = fish;
41     swimable.swim;
42   }
43 }
44  
45 /**
46 输出结果:
47 Fish eagle walk with speed : 100.
48 Fish eagle fly fast.
49 Fish eagle swim fast.
50 fish eagle has father ? true.
51  
52 Fish eagle fly fast.
53 Fish eagle swim fast.
54 */

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

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

  在对象中混入trait

/**
 * 单独的日志模块
 * 只是标识要记录日志,但没有明确定义如何记录日志
 */
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 : 业务操作
*/

三、总结

  简单来说,Scala的trait就是类似于Java的接口。使一个类能实现多种功能。

posted @ 2017-11-11 20:51  yuanninesuns  阅读(851)  评论(0编辑  收藏  举报