Scala 基礎(5)—— 構建函數式對象

有了 Scala 基礎(4)—— 類和對象 的前提,如今就能夠來構建一個基於 Scala 的函數式對象。html

下面開始構造一個有理數對象 Rational。java

 

1. 主構造方法和輔助構造方法ide

對於每個類的定義,Scala 只容許一個主構造方法,主構造方法的入參緊跟在類定義的後面:函數

class Rational(n: Int, d: Int) {

}

val r = new Rational(1,2) // 有理數 1/2

 

你可能須要一個分母 d 被預約義爲 1 的一個構造方法,這事就須要用到輔助構造方法(auxiliary constructor)。post

輔助構造方法以 def this(...) 開始:ui

class Rational(n: Int, d: Int) {

  def this(n: Int) = this(n, 1)
}

val r = new Rational(5) // 有理數5

 

2. 前置條件this

你可能須要對初始化一個對象進行一些參數檢查,例若有理數的分母 d 不能爲 0。url

在每個 Scala 文件中都會自動引用 Predef 這個獨立對象,裏面有 require() 方法可以知足這個需求。spa

class Rational(n: Int, d: Int) {
  require(d != 0)

def this(n: Int) = this(n, 1) } val r = new Rational(5, 0) // 會拋出異常

 

3. toString() 方法htm

對於一個類來講,重寫 toString() 方法是一個很常見的需求。

須要注意的是 Scala 對於重寫父類方法,會強制加上 override,而在 Java 中這一行爲是 optional 的。

class Rational(n: Int, d: Int) {
  require(d != 0)

def this(n: Int) = this(n, 1)
override def toString: String = n + "/" + d // 去掉 override 會編譯報錯 }

 

 

4. 添加字段和方法

在這裏爲 Rational 類添加一個 add() 方法,這裏會出現一個初學者常見的錯誤:

class Rational(n: Int, d: Int) {
  require(d != 0)

  def this(n: Int) = this(n, 1)

  override def toString: String = n + "/" + d

  def add(that: Rational): Rational = new Rational(n * that.d + that.n * d, d * that.d)
}

這段邏輯看似沒有問題,並且編譯期間也不會報錯,可是運行時卻會報錯。

緣由在於,雖然當前對象 this 能夠很天然地使用構造方法傳入的 n 和 d,可是 that 對象卻不能這麼使用。

這時就要增長字段:

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: String = n + "/" + d

  def add(that: Rational): Rational = new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
}

 

對於有理數來講,咱們不但願出現 2/10 這樣的數字,而是 1/5,這時就要增長私有的方法 gcd 來求最大公約數,以及私有字段 g 存儲最大公約數:

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)

  override def toString: String = numer + "/" + denom

  def add(that: Rational): Rational = new Rational(numer * that.denom + that.numer * denom, denom * that.denom)

  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}

 

5. 定義操做符

Scala 能夠用定義方法一樣的方式定義操做符,達到相似「操做符重載」的效果。

例如,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)

  override def toString: String = numer + "/" + denom

  def +(that: Rational): Rational = new Rational(numer * that.denom + that.numer * denom, denom * that.denom)

  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}

val r1 = new Rational(5, 10)
val r2 = new Rational(3, 10)
println(r1 + r2) // 能夠用+操做符,結果是 4/5

 

由此處可見,Scala 中的標識符分爲2種:

  • 字母數字組合標識符:規則基本與 Java 相同,推薦駝峯命名法。
  • 操做標識符:由一個或多個操做字符構成。

 

6. 方法重載

與 Java 相同,Scala 支持相同方法名,不一樣的參數列表的方法重載:

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)

  override def toString: String = numer + "/" + denom

  def +(that: Rational): Rational = new Rational(numer * that.denom + that.numer * denom, denom * that.denom)

  def +(that: Int): Rational = new Rational(numer + that * denom, denom)

  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}
相關文章
相關標籤/搜索