zoukankan      html  css  js  c++  java
  • 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的接口。使一个类能实现多种功能。

  • 相关阅读:
    Ruby
    WebGL的第二个小程序
    wegGL的第一个小程序
    Node.js介绍
    接口隔离原则(Interface Sepreation Principle)
    参数
    字段/属性
    接口和抽象类
    javascript中的事件
    线性回归算法-4.多元线性回归算法
  • 原文地址:https://www.cnblogs.com/yuanninesuns/p/7819948.html
Copyright © 2011-2022 走看看