4-函数式对象-Scala

前言

本节将展示类参数、构造方法、方法、操作符、私有成员、重写、前置条件检查、重载和自引用。
环境:
Windows + Scala-2.12.8

1. Rational 类的规格定义

Rational 即有理数,即 1 / 2 的形式表示的数。下面将使用构建Rational 类来实现加、减、乘、除。
另外,设计的Rational 是不可变的对象,即不能再原有的对象中改变,而是经过计算后生成一个新的对象。

2. 构建Rational

Rational 类:

// 非字段或方法的代码被编译到主构造方法中
class Rational(n: Int, d: Int) {
  println("Created " + n + "/" + d)
}

这段代码中,n 和 d 称为类参数,类参数为 val 类型,编译器会接收这两个参数,并创建一个主构造方法同样接收这两个参数,即在该类中所有的方法都可以使用 n 和 d。
另外,非字段或方法的代码被编译到主构造方法中,上面代码中println 会在创建主构造方法时执行,如下:
实例化一个Rational:
在这里插入图片描述

3.重写toString

上面结果返回了,Rational@23dc70c1, 是继承java.lang.Object 的toString方法,现在将其重写:

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

在这里插入图片描述

4. 检查前置条件

现在,还不能判断分母为0的情况:
在这里插入图片描述
使用Require 检查分母不为0:

// 检查前置条件
class Rational(n: Int, d: Int) {
  require(d != 0)
  override def toString = n + "/" + d
}

在这里插入图片描述

5. 添加字段

在第2 点中说了,n 和 d 可以在Rational中都可以使用,但是在外部却不可以使用,如:
在这里插入图片描述
需要添加字段:

// 添加字段
class Rational(n: Int, d: Int) {
  require(d != 0)
  val number: Int = n
  val denom: Int = d
  override def toString = number + "/" + denom
}

在这里插入图片描述
同时,在这里添加一个add 方法,用于加法:

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

在这里插入图片描述

6. 自引用

自引用与Java类似,同样使用this 关键字表示当前被构造的对象实例。
如,构造一个lessThan 方法,用来比较Rationl的大小:

// 自引用
def lessThan(that: Rational) = 
  this.number * that.denom < that.number * this.denom

7. 辅助构造方法

在Java中,可以通过重写构造方法实现不同参数类型、个数的实例化,在Scala 中可以使用辅助构造方法(除了主构造方法之外的构造方法都成为辅助构造方法)。例如,这里实现接收一个Int 类型的辅助构造方法:

// 辅助构造方法
class Rational(n: Int, d: Int) {
  require(d != 0)
  val number: Int = n
  val denom: Int = d
  def this(n: Int) = this(n, 1) // 辅助构造方法
  override def toString = number + "/" + denom
  def add(that: Rational): Rational =
    new Rational(
      number * that.denom + that.number * denom,
      denom * that.denom
    )
}

辅助构造方法与Java中类似,但更加严格。在辅助构造方法中必须调用在该辅助构造方法之前的构造方法,如上述代码中调用了主构造方法: this(n, 1)。这样造成的结果就是,每个构造方法都会用到主构造方法,所以,主构造方法就是该类的单一入口。
在这里插入图片描述

8. 私有字段和方法

在前面的代码中,还不能实现约分,及new Ratioanl(2, 4) 返回的结果就是 2/4,我们想要的效果是返回 1/2。所以,添加私有方法gcd 求取最大公约数:

// 私有字段和方法
class Rational(n: Int, d: Int) {
  require(d != 0)
  private val g = gcd(n.abs, d.abs)
  val number: Int = n / g
  val denom: Int = d / g
  def this(n: Int) = this(n, 1)
  override def toString = number + "/" + denom
  def add(that: Rational): Rational =
    new Rational(
      number * that.denom + that.number * denom,
      denom * that.denom
    )
  // 递归求最大公约数
  private def gcd(a: Int, b: Int): Int =
    if (b == 0) a else gcd(b, a % b)
}

在这里插入图片描述

9. 定义操作符

在Scala 中 1 + 2就是调用了Int 类型的+ 方法,因此这里将add 改为 + ,定义 + 操作符,同时定义 * :

// 定义操作符
class Rational(n: Int, d: Int) {
  require(d != 0)
  private val g = gcd(n.abs, d.abs)
  val number: Int = n / g
  val denom: Int = d / g
  def this(n: Int) = this(n, 1)
  override def toString = number + "/" + denom
  def + (that: Rational): Rational =
    new Rational(
      number * that.denom + that.number * denom,
      denom * that.denom
    )
  def * (that: Rational): Rational =
    new Rational(
      number * that.number, denom * that.denom)
  // 递归求最大公约数
  private def gcd(a: Int, b: Int): Int =
    if (b == 0) a else gcd(b, a % b)
}

示例:
在这里插入图片描述
同样,在这里,仍然存在 * 比 + 拥有更高的优先级:
在这里插入图片描述

10. Scala中的标识符

这里介绍Scala中四种标识符的形式:

  1. 字母数字组合标识符
    以包含字母、数字、下划线。与Java一样采用驼峰命名法。
    字段、方法参数、局部变量和函数第一个字母小写,类和特质第一个字母大写。
    在Java中,常量是全部大写以_ 隔开,在Scala中,常量使用首字母大写的驼峰命名。
  2. 操作标识符
    由一个或多个操作字母构成。如 + ++ ::: <?> :->
  3. 混合标识符
    由一个字母数字组合操作符、一个下划线和一个符号操作符组成。如,unary_+ 表示+ 操作符的方法名。
  4. 字面标识符
    用反括号括起来的任意字符串,如`yield(Markdown不能写两个反括号,注意yield 前后都有反括号),在Scala中 yield 是关键字,如果要使用,就可以使用

11. 方法重载

现在,我们可以实现 a * a,但无法实现 a * 2,因为我们* 接受的参数是 that: Rational,我们需要重载一个 i: Int
在这里插入图片描述
同时,补充上减和除:

// 方法重载
class Rational(n: Int, d: Int) {
  require(d != 0)
  private val g = gcd(n.abs, d.abs)
  val number: Int = n / g
  val denom: Int = d / g
  def this(n: Int) = this(n, 1)
  override def toString = number + "/" + denom
  def + (that: Rational): Rational =
    new Rational(
      number * that.denom + that.number * denom,
      denom * that.denom
    )
  def + (i: Int): Rational =
    new Rational(
      number + i * denom, denom)
  def - (that: Rational): Rational =
    new Rational(
      number * that.denom - that.number * denom,
      denom * that.denom
    )
  def - (i: Int): Rational =
    new Rational(
      number - i * denom, denom)

  def * (that: Rational): Rational =
    new Rational(
      number * that.number, denom * that.denom)
  def * (i: Int): Rational =
    new Rational(
      number * i, denom)
  def / (that: Rational): Rational =
    new Rational(
      number * that.denom, denom * that.number)
  def / (i: Int): Rational =
    new Rational(
      number, denom * i)
  // 递归求最大公约数
  private def gcd(a: Int, b: Int): Int =
    if (b == 0) a else gcd(b, a % b)
}

使用重载:
在这里插入图片描述
在这里插入图片描述

12. 隐式转换

上面代码,我们实现了 a * 2,实际上是 a.* (2) 的形式执行,但如果 执行 2 * 2,就是 2.*(a),这样会报错的,因为,原始的Int 类型并没有接受Rational 类型的乘法:
在这里插入图片描述
解决方法就是使用隐式转换,可以理解为:如果靠现有的方法、类型并不能满足操作,会尝试隐式转换,如果隐式转换仍然不行,就会抛出错误
在编译器执行,与Rational类同级别,而不是在Rational 类里面:

// 隐式转换
implicit def intToRational(x: Int) = new Rational(x)

在这里插入图片描述

本节代码可见GitHub,3-Functional object:

https://github.com/GYT0313/Scala-Learning

完!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章