zoukankan      html  css  js  c++  java
  • scala 学习笔记(05) OOP(中)灵活的trait

    trait -- 不仅仅只是接口!

    上回继续,scala是一个非常有想法的语言,从接口的设计上就可以发现它的与众不同。scala中与java的接口最接近的概念是trait,见下面的代码:

    package yjmyzz
    
    
    object App {
    
      def main(args: Array[String]) {
        val bird = Bird("pigeon")
        bird.fly
        println(bird.isInstanceOf[Bird]) //true
        println(bird.isInstanceOf[Flyable]) //true
        println(bird.toString) //this is a Bird:pigeon
        bird.test //hello
      }
    }
    
    /**
     * 定义一个"接口"
     */
    trait Flyable {
    
      /**
       * 定义接口方法
       */
      def fly;
    
    
      /**
       * 接口中也有可以方法的实现(是不是惊呆了!)
       * @return
       */
      def test = {
        println("hello")
      }
    }
    
    
    class Bird(var name: String) extends Flyable {
      /**
       * 实现接口中的方法
       */
      def fly: Unit = {
        println("I am a " + name + ", and I can fly~")
      }
    
      override def toString = {
        "this is a Bird:" + name
      }
    }
    
    object Bird {
      def apply(name: String) = {
        new Bird(name)
      }
    
    }
    

    从上面的代码中,可以看出trait与java中interface的异同,相同的是如果把trait单纯当接口来用,trait中只需要定义方法签名即可,然后由"子类"来实现。不同的是,scala中的trait里也可以有方法实现!而且实现接口时,关键字不是implements而是extends(当然,还可能是with,后面还会提到),这说明scala中trait并不仅仅只是接口,它也是一种特殊的类

    trait的mixin:

    trait还有一个神奇的特性,可以在运行时,动态与其它类合体!见下面的代码:

    package yjmyzz
    
    
    object App {
    
      def main(args: Array[String]) {
        val duck = new Bird("duck") with Swim
        println(duck.isInstanceOf[Flyable]) //true
        println(duck.isInstanceOf[Swim]) //true 注意这里:表示此时Bird也是一个Swim实例
        duck.fly //I am a duck, and I can fly~
        duck.swim //I can swim! 注:是不是很有意思,绝对的动态晚绑定!
      }
    }
    
    /**
     * 定义一个"接口"
     */
    trait Flyable {
    
      /**
       * 定义接口方法
       */
      def fly;
    
    
      /**
       * 接口中也有可以方法的实现(是不是惊呆了!)
       * @return
       */
      def test = {
        println("hello")
      }
    }
    
    
    /**
     * 再来一个"接口"
     */
    trait Swim {
      def swim = {
        println("I can swim!")
      }
    }
    
    
    class Bird(var name: String) extends Flyable {
    
      /**
       * 实现接口中的方法
       */
      def fly: Unit = {
        println("I am a " + name + ", and I can fly~")
      }
    
      override def toString = {
        "this is a Bird:" + name
      }
    }
    

    我们又新增了一个trait:Swim,然后注意第7行,通过with Swim,硬是把跟Swim毫不相关的Bird实例跟它搅在一起了,然后实例duck就获得了Swim的能力! 这种场景下,trait就不应该理解为接口,而应该认为它是一种特性,是一种可以动态赋予其它实例的超能力!(这也是为什么关键字不叫interface,而是叫trait的原因吧)

    trait与java中的接口还有一个明显的区别,trait可以继承自类,java中的interface可是不允许继承自class的! 见下面的代码示例:

    package yjmyzz
    
    /**
     * 动物基类
     */
    class Animal {}
    
    /**
     * 会飞的动物("接口"继承类)
     */
    trait FlyAnimal extends Animal {
      def fly;
    }
    
    /**
     * 即会飞又会游泳的动物("接口"继承"接口")
     */
    trait SwimAndFlyAnimal extends FlyAnimal {
      def swim;
    }
    
    /**
     * 会说话(注:这个接口是完全独立的,不继承自任何其它类或trait)
     */
    trait Talk {
      def talk;
    }
    
    /**
     * 鸟(继承类,又实现"接口",实际上是多重继承)
     */
    class Bird(var birdName: String) extends Animal with FlyAnimal {
      override def fly: Unit = {
        println(birdName + "能飞!")
      }
    }
    
    /**
     * 被继承的class[Animal]与trait[Talk]相互之间也可以没半毛钱关系
     */
    class AlienAnimal extends Animal with Talk {
      override def talk: Unit = println("外星动物很厉害的啦,它们会说话!")
    }
    
    /**
     * 类也可以直接继承自trait
     */
    class TalkThing extends Talk {
      override def talk: Unit = println("我也不知道我是啥,反正我会说话!")
    }
    
    object ScalaApp {
    
      def main(args: Array[String]) {
    
        var eagle = new Bird("老鹰")
        eagle.fly
    
        println
    
        var swan = new Bird("天鹅") with SwimAndFlyAnimal {
          override def swim: Unit = println("天鹅还能游泳!")
        }
        swan.fly
        swan.swim
    
        println
    
        var sparrow = new Bird("麻雀") with Talk {
          override def talk: Unit = println {
            "麻雀'说话',就是叽叽喳喳的叫!"
          }
        }
        sparrow.fly
        sparrow.talk
    
        println
    
        var alienAnimal = new AlienAnimal
        alienAnimal.talk
    
        println
    
        var talkThing = new TalkThing
        talkThing.talk
    
    
      }
    
    }
    

    运行结果:

    老鹰能飞!

    天鹅能飞!
    天鹅还能游泳!

    麻雀能飞!
    麻雀'说话',就是叽叽喳喳的叫!

    外星动物很厉害的啦,它们会说话!

    我也不知道我是啥,反正我会说话!


    关于trait,小结一下:

    1、trait"类似"(注:仅仅只是类似)java中的接口,可以只定义方法签名,交由子类去实现

    2、trait中也可以有具体的方法实现

    3、trait可以继承自trait,也可以继承自class

    4、class也可以直接继承自trait

    5、trailt可以在运行时,通过with关键,动态混入class实例

  • 相关阅读:
    限制泛型可用类型,类型通配符声明,泛型方法
    泛型简介,泛型类及使用
    异常概念和处理机制,try-catch-finally,throw和throws,自定义异常
    随机验证码
    常用类--Date日期类,SimpleDateFormat日期格式类,Calendar日历类,Math数学工具类,Random随机数类
    String、StringBuffer和StringBuilder,定义一个自己的StringBuilder的类
    自动装箱和拆箱,枚举类型
    使用内部类开发一个存放数据的容器
    手推期望、方差
    ML 徒手系列 最大似然估计
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/4719256.html
Copyright © 2011-2022 走看看