• 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命令
      这里写图片描述

  • 相关阅读:
    1074 食物链 (并查集)
    2832 6个朋友
    病毒 (拓扑)
    4735 烦人的幻灯片 (拓扑)
    JavaScript中变量的LHS引述和RHS引用
    td自动换行
    SQL Server 中的 NOLOCK 到底是什么意思?
    jQuery中遇到的坑
    jQuery中attr()函数 VS prop()函数
    Javascript刷新页面的几种方法
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300224.html
Copyright © 2020-2023  润新知