有了前面的 Scala 的基本知識(shí),本篇介紹如何定義完整功能的 Scala 類定義。本篇著重介紹如果定義 Functional objects (函數(shù)化對(duì)象或是方程化對(duì)象),函數(shù)化對(duì)象指的是所定義的類或?qū)ο蟛话魏慰梢孕薷牡臓顟B(tài)。本篇定義了一個(gè)有理數(shù)類定義的幾個(gè)不同版本以介紹 Scala 類定義的幾個(gè)特性:類參數(shù)和構(gòu)造函數(shù),方法,操作符,私有成員,重載,過載,條件檢查,引用自身。
首先,我們回憶下有理數(shù)的定義:一個(gè)有理數(shù)(rational)可以表示成個(gè)分?jǐn)?shù)形式: n/d, 其中 n 和 d 都是整數(shù)(d 不可以為 0),n 稱為分子(numberator),d 為分母(denominator)。和浮點(diǎn)數(shù)相比,有理數(shù)可以精確表達(dá)一個(gè)分?jǐn)?shù),而不會(huì)有誤差。
因此我們定義的 Rational 類支持上面的有理數(shù)的定義。支持有理數(shù)的加減乘除,并支持有理數(shù)的規(guī)范表示,比如 2/10,其規(guī)范表示為 1/5。分子和分母的最小公倍數(shù)為 1。
有了有理數(shù)定義的實(shí)現(xiàn)規(guī)范,我們可以開始設(shè)計(jì)類 Rational,一個(gè)好的起點(diǎn)是考慮用戶如何使用這個(gè)類,我們已經(jīng)決定使用“Immutable”方式來使用 Rational 對(duì)象,我們需要用戶在定義 Rational 對(duì)象時(shí)提供分子和分母。因此我們可以開始定義 Rational 類如下:
class Rational( n:Int, d:Int)
可以看到和 Java 不同的,Scala 的類定義可以有參數(shù),稱為類參數(shù),如上面的 n,d。Scala 使用類參數(shù),并把類定義和主構(gòu)造函數(shù)合并在一起,在定義類的同時(shí)也定義了類的主構(gòu)造函數(shù)。因此 Scala 的類定義相對(duì)要簡(jiǎn)潔些。
Scala 編譯器會(huì)編譯 Scala 類定義包含的任何不屬于類成員和類方法的其它代碼,這些代碼將作為類的主構(gòu)造函數(shù),比如我們定義一條打印消息作為類定義的代碼:
scala> class Rational (n:Int, d:Int) {
| println("Created " + n + "/" +d)
| }
defined class Rational
scala> new Rational(1,2)
Created 1/2
res0: Rational = Rational@22f34036
可以看到創(chuàng)建 Ratiaonal 對(duì)象時(shí),自動(dòng)執(zhí)行類定義的代碼(主構(gòu)造函數(shù))。
上面的代碼創(chuàng)建 Rational(1,2),Scala 編譯器打印出 Rational@22f34036,這是因?yàn)槭褂昧巳笔〉念惖?toString()定義(Object 對(duì)象的),缺省實(shí)現(xiàn)是打印出對(duì)象的類名稱+“@”+16 進(jìn)制數(shù)(對(duì)象的地址),顯示結(jié)果不是很直觀,因此我們可以重新定義類的 toString()方法以顯示更有意義的字符。
在 Scala 中,你也可以使用 override 來重載基類定義的方法,而且必須使用 override 關(guān)鍵字表示重新定義基類中的成員。比如:
scala> class Rational (n:Int, d:Int) {
| override def toString = n + "/" +d
| }
defined class Rational
scala> val x= new Rational(1,3)
x: Rational = 1/3
scala> val y=new Rational(5,7)
y: Rational = 5/7
前面說過有理數(shù)可以表示為 n/d (其中 d,n 為證書,而 d 不能為 0),對(duì)于前面的 Rational 定義,我們?nèi)绻褂?0,也是可以的
scala> new Rational(5,0)
res0: Rational = 5/0
怎么解決分母不能為 0 的問題了,面向?qū)ο缶幊痰囊粋€(gè)優(yōu)點(diǎn)是實(shí)現(xiàn)了數(shù)據(jù)的封裝,你可以確保在其生命周期過程中是有效的。對(duì)于有理數(shù)的一個(gè)前提條件是分母不可以為 0,Scala 中定義為傳入構(gòu)造函數(shù)和方法的參數(shù)的限制范圍,也就是調(diào)用這些函數(shù)或方法的調(diào)用者需要滿足的條件。Scala 中解決這個(gè)問題的一個(gè)方法是使用 require 方法(require 方法為 Prede f對(duì)象的定義的一個(gè)方法,Scala 環(huán)境自動(dòng)載入這個(gè)類的定義,因此無需使用 import 引入這個(gè)對(duì)象),因此修改 Rational 定義如下:
scala> class Rational (n:Int, d:Int) {
| require(d!=0)
| override def toString = n + "/" +d
| }
defined class Rational
scala> new Rational(5,0)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:211)
... 33 elided
可以看到如果再使用 0 作為分母,系統(tǒng)將拋出 IllegalArgumentException 異常。
更多建議: