zoukankan      html  css  js  c++  java
  • Programming In Scala笔记-第六章、函数式对象

      这一章主要是以定义和完善一个有理数类Rational为线索,分析和介绍有关类定义,构造函数,方法重写,变量定义和私有化,以及对操作符的定义等。

    一、Rational类定义和构造函数

    1、定义一个空类

    class Rational(n: Int, d: Int)

      如果一个class没有函数体时,可以不用写花括号,上面的代码是最简形式。圆括号中的n和d是类参数,Scala编译器会根据这两个类参数,为该类生成一个对应的包含两个参数的主构造函数。
      在文本编辑器中输入以上代码后,保存为Rational.scala类型,使用scalac命令进行编译

    scalac Rational.scala

      然后使用javap -private类名的方式查看编译后的class文件内容

    javap -private Rational

      反编译后的结果如下
      这里写图片描述

    2、主构造函数定义
      上面对Rational类的定义是最简单的形式。如果需要往主构造函数中增加逻辑,可以在类定义之后用花括号包含一些代码

    class Rational(n: Int, d: Int) {
      println("Created "+ n +"/"+ d)
    }

      编译后,使用jd.exe查看反编译的代码,这一段println语句被加载到了主构造函数中。
      这里写图片描述

      使用这个类定义构造一个Rational对象,
      这里写图片描述

    3、辅助构造函数的定义
      有时候一个类除了主构造函数之外,还需要定义多个不同形式的辅助构造函数。上面定义的主构造函数中,需要传入分子和分母两个参数。接下来定义一个只接收一个参数的构造函数,如果传入一个参数,则分母取默认值1。

    class Rational(n: Int, d: Int) {
      require(d != 0)
      val numer: Int = n
      val denom: Int = d
    
      def this(n: Int) = this(n, 1)
    
      override def toString = numer + "/" + denom
      def add(that: Rational): Rational =
        new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
    }

      在Scala中每一个辅助构造函数的定义中必须首先调用另外一个构造函数,不管是另外一个辅助构造函数,还是主构造函数。所以,任何一个构造函数,最终都会直接或间接的调用了主构造函数。

    二、重写toString方法

      上面的代码中,new一个Rational对象后得到的返回值res0会调用其默认的toString方法。如果需要重写toString

    class Rational(n: Int, d: Int) {
      override def toString = n + "/" + d
    }

      反编译后如下
      这里写图片描述
      初始化一个Rational对象,重写方法与Java中类似,需要在前面加一个override关键字。
      这里写图片描述
      

    三、设置类的先决条件

      有理数可以写成分子/分母的形式,需要保证分母不为0。在不加这个限制条件时,
      这里写图片描述

      可以实现一个require方法来达到这个目的,require接收一个boolean类型的参数
      

    class Rational(n: Int, d: Int) {
      require(d != 0)
      override def toString = n + "/" + d
    }

      此时再次执行上面的new Rational(1, 0),就会出现如下报错提示
      这里写图片描述

    四、定义变量

      接下来为Rational类定义一个add方法,该方法可以接收另一个Rational类型的对象并计算两者的和,返回一个求和后的Rational对象。
      按照Java中的思想,定义如下

    class Rational(n: Int, d: Int) {
      require(d != 0)
      override def toString = n + "/" + d
      def add(that: Rational): Rational = 
        new Rational(n * that.d + that.n * d, d * that.d)
    }

      但是上面这段代码,在编译的时候就会报错
      这里写图片描述

      正确的定义如下,

    class Rational(n: Int, d: Int) {
      require(d != 0)
      val numer: Int = n
      val denom: Int = d
      override def toString = numer + "/" + denom
      def add(that: Rational): Rational =
        new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
    }

      执行下面三行代码

    val oneHalf = new Rational(1, 2)
    
    val twoThirds = new Rational(2, 3)
    
    oneHalf add twoThirds

      结果如下
      这里写图片描述

      也可以直接访问某个对象的变量值,

    val r = new Rational(1, 2)
    r.numer
    r.denom

      这里写图片描述

    五、私有变量和方法

      写成分子除以分母形式的有理数,可以根据分子和分母的最大公约数进行化简。为此,可以在Rational类中定义一个求两个整数最大公约数的方法,这个方法只在Rational内部调用,可以定义为private类型,防止外部调用。得到的最大公约数也定义为private类型。

    class Rational(n: Int, d: Int) {
      require(d != 0)
    
      private val g = gcd(n.abs, d.abs)
      val numer: Int = n / g
      val denom: Int = d / g
    
      def this(n: Int) = this(n, 1)
    
      def add(that: Rational): Rational =
        new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
    
      override def toString = numer + "/" + denom
    
      private def gcd(a: Int, b: Int): Int =
        if (b == 0) a else gcd(b, a %b)
    }

    六、this关键字

      接下来,如果需要为Rational对象增加一个lessThan方法用于比较两个Rational对象的大小,增加一个max方法,用于获取两个Rational对象中值最大的那个。可以使用如下代码

    def lessThan(that: Rational) = 
      this.numer * that.denom < that.number * this.denom
    
    def max(that: Rational) = 
      if (this.lessThan(that)) that else this

      代码中的this关键字,指向当前对象本身。

    七、定义操作符

      上面定义了Rational类型的add方法用于求两个有理数之和,add方法的使用是a add b,其中a和b都是Rational类型的。如果a和b都是int或者double类型,求两者之和直接是a + b的形式,那么如何使Rational类型变量也支持+操作呢?
      在前面的博客中提到过a + b实际上是a.+(b)的形式,如果把add方法直接命名成+,如下

    class Rational(n: Int, d: Int) {
      require(d != 0)
    
      private val g = gcd(n.abs, d.abs)
      val numer: Int = n / g
      val denom: Int = d / g
    
      def this(n: Int) = this(n, 1)
    
      def + (that: Rational): Rational =
        new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
    
      def * (that: Rational): Rational =
        new Rational(numer * that.numer, denom * that.denom)
      override def toString = numer + "/" + denom
    
      private def gcd(a: Int, b: Int): Int =
        if (b == 0) a else gcd(b, a %b)
    }

      调用+*方法

    val x = new Rational(1, 2)
    val y = new Rational(2, 3)
    x + y
    x * y

      结果如下:
      这里写图片描述

    八、方法重载

      有时候,需要定义多个方法名相同,但是参数类型或个数不相同的重载方法。比如上一步中的+*方法,都必须接收Rational类型的参数,如果想要传递一个Int型参数进行求和或求积运算,程序就会报错。为此,需要重新定义方法名为+*,但是接收参数为Int型的两个方法。

    class Rational(n: Int, d: Int) {
      require(d != 0)
    
      private val g = gcd(n.abs, d.abs)
      val numer: Int = n / g
      val denom: Int = d / g
    
      def this(n: Int) = this(n, 1)
    
      def + (that: Rational): Rational =
        new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
    
      def + (i: Int): Rational =
        new Rational(numer + I * denom, denom)
    
      def * (that: Rational): Rational =
        new Rational(numer * that.numer, denom * that.denom)
    
      def * (i: Int): Rational =
        new Rational(numer * i, denom)
    
      override def toString = numer + "/" + denom
    
      private def gcd(a: Int, b: Int): Int =
        if (b == 0) a else gcd(b, a %b)
    }

    九、Implicit关键字隐式转换

      经过上面的定义之后,可以支持Rational类型变量乘以Int变量的操作了。

    val r = new Rational(1, 2)
    r * 2

      结果如下
      这里写图片描述
      但是,如果反过来,输入2 * r是会报错的,
      这里写图片描述

      这是由于2 * r实质上调用的是2的*方法,而对于Int类型的2来说,是不支持传入一个Rational类型变量做乘法的。

      如果需要支持这种用法,可以使用implict关键字加入如下一行代码

    implicit def intToRational(x: Int) = new Rational(x)

      上面这一行代码会告诉Scala编译器自动使用该方法处理Int型的变量,重新执行2 * r命令
      这里写图片描述

  • 相关阅读:
    document.getElementById("mytxt").style.left=""style.left在IE的FF中注意
    asp.net 用户控件中 使用相对路径的解决方法 图片路径问题(用户控件、图片路径) ,ResolveUrl
    探索 Block (一) (手把手讲解Block 底层实现原理)
    iOS 多线程开发 (概念与API简介)
    iOS 性能小点
    iOS runtime (二)(runtime学习之AutoCoding源码分析)
    探索 NSRunLoop (二)(NSRunLoop 自己动手实现SimpleRunLoop)
    iOS NSNotificationCenter (自己实现一个通知中心XMCNotificationCenter)
    iOS runtime (三)(runtime学习之YYModel源码分析)
    iOS runtime(一)(runtime 分析理解)
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300224.html
Copyright © 2011-2022 走看看