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

完!

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