鍍金池/ 教程/ Scala/ 類和對(duì)象 (三)
包對(duì)象
Ordered Trait
組合和繼承–定義 final 成員
基本數(shù)據(jù)類型
Match 表達(dá)式
類和對(duì)象 (三)
操作基本數(shù)據(jù)類型
for 表達(dá)式
組合和繼承–重載成員函數(shù)和方法
類和對(duì)象 (二)
組合和繼承–定義 factory 對(duì)象
組合和繼承–多態(tài)和動(dòng)態(tài)綁定
Trait 的基本概念
if 表達(dá)式
組合和繼承–抽象類
函數(shù)–函數(shù)字面量的一些簡(jiǎn)化寫法
while 循環(huán)
組合和繼承–使用組合還是繼承?
訪問(wèn)控制修飾符
Trait 示例–Rectangular 對(duì)象
組合和繼承–定義參數(shù)化成員變量
組合和繼承–定義無(wú)參數(shù)方法
類和對(duì)象 (一)
函數(shù)–閉包
函數(shù)–類成員函數(shù)
Scala 基本數(shù)據(jù)類型的實(shí)現(xiàn)方法
try 表達(dá)式處理異常
選擇瘦接口還是胖接口設(shè)計(jì)?
組合和繼承–小結(jié)
創(chuàng)建新的控制結(jié)構(gòu)
使用 import
為訪問(wèn)控制修飾符添加作用域
Scala 的類層次關(guān)系
類和對(duì)象 (五)
傳名參數(shù)
柯里化函數(shù)
函數(shù)–頭等公民
組合和組合和繼承–定義 heighten 和 widen 函數(shù)
使用 Package–將代碼放入包中
隱含的 import
所有類的公共子類–底層類型
進(jìn)一步 Scala
函數(shù)–局部函數(shù)
引用包中的代碼
組合和繼承–使用 override 修飾符
組合和繼承–實(shí)現(xiàn)類 Element 的 above,beside 和 toString()方法
類和對(duì)象 (四)
函數(shù)–尾遞歸
沒(méi)有“break”和“continue”的日子
組合和繼承–調(diào)用基類構(gòu)造函數(shù)
減低代碼重復(fù)
函數(shù)–函數(shù)–可變參數(shù),命名參數(shù),缺省參數(shù)
起步 Scala
組合和繼承–擴(kuò)展類
函數(shù)–部分應(yīng)用的函數(shù)
開始神奇的 Scala編程之旅
組合和繼承–概述
Trait 用來(lái)實(shí)現(xiàn)可疊加的修改操作

類和對(duì)象 (三)

有了前面的 Scala 的基本知識(shí),本篇介紹如何定義完整功能的 Scala 類定義。本篇著重介紹如果定義 Functional objects (函數(shù)化對(duì)象或是方程化對(duì)象),函數(shù)化對(duì)象指的是所定義的類或?qū)ο蟛话魏慰梢孕薷牡臓顟B(tài)。 本篇定義了一個(gè)有理數(shù)類定義的幾個(gè)不同版本以介紹 Scala 類定義的幾個(gè)特性:類參數(shù)和構(gòu)造函數(shù),方法,操作符,私有成員,重載,過(guò)載,條件檢查,引用自身。

Rational類定義規(guī)范

首先,我們回憶下有理數(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。

定義Rational

有了有理數(shù)定義的實(shí)現(xiàn)規(guī)范,我們可以開始設(shè)計(jì)類 Rational,一個(gè)好的起點(diǎn)是考慮用戶如何使用這個(gè)類,我們已經(jīng)決定使用“Immutable”方式來(lái)使用 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ù))。

重新定義類的toString 方法

上面的代碼創(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 來(lái)重載基類定義的方法,而且必須使用 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

前提條件檢查

前面說(shuō)過(guò)有理數(shù)可以表示為 n/d (其中 d,n 為證書,而 d 不能為 0),對(duì)于前面的 Rational 定義,我們?nèi)绻褂?0,也是可以的

scala> new Rational(5,0)
res0: Rational = 5/0

怎么解決分母不能為 0 的問(wèn)題了,面向?qū)ο缶幊痰囊粋€(gè)優(yōu)點(diǎn)是實(shí)現(xiàn)了數(shù)據(jù)的封裝,你可以確保在其生命周期過(guò)程中是有效的。對(duì)于有理數(shù)的一個(gè)前提條件是分母不可以為 0,Scala 中定義為傳入構(gòu)造函數(shù)和方法的參數(shù)的限制范圍,也就是調(diào)用這些函數(shù)或方法的調(diào)用者需要滿足的條件。Scala 中解決這個(gè)問(wèn)題的一個(gè)方法是使用 require 方法(require 方法為 Prede f對(duì)象的定義的一個(gè)方法,Scala 環(huán)境自動(dòng)載入這個(gè)類的定義,因此無(wú)需使用 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 異常。