新聞中心
學(xué)習(xí)Scala中Rational類的下一步是,我們將把視線轉(zhuǎn)向當(dāng)前主構(gòu)造器行為里的一些問(wèn)題。如本章早些時(shí)候提到的,分?jǐn)?shù)的分母不能為零。然而目前主構(gòu)造器會(huì)接受把零傳遞給d:

編輯推薦:Scala編程語(yǔ)言專題
- scala> new Rational(5, 0)
- res6: Rational = 5/0
面向?qū)ο缶幊痰囊粋€(gè)優(yōu)點(diǎn)就是它允許你把數(shù)據(jù)封裝在對(duì)象之內(nèi)以便于你確保數(shù)據(jù)在整個(gè)生命周期中是有效的。像Rational這樣的不可變對(duì)象,這就意味著你必須確保在對(duì)象創(chuàng)建的時(shí)候數(shù)據(jù)是有效的(并且,確保對(duì)象的確是不可變的,這樣數(shù)據(jù)就不會(huì)在之后變成無(wú)效的狀態(tài))。由于零做分母對(duì)Rational來(lái)說(shuō)是無(wú)效狀態(tài),因此在把零傳遞給d的時(shí)候,務(wù)必不能讓Rational被構(gòu)建出來(lái)。
解決這個(gè)問(wèn)題的***辦法是為主構(gòu)造器定義一個(gè)先決條件:precondition說(shuō)明d必須為非零值。先決條件是對(duì)傳遞給方法或構(gòu)造器的值的限制,是調(diào)用者必須滿足的需求。一種方式是使用require方法,require方法定義在scala包里的孤立對(duì)象Predef上。如:
- class Rational(n: Int, d: Int) {
- require(d != 0)
- override def toString = n +"/"+ d
- }
require方法帶一個(gè)布爾型參數(shù)。如果傳入的值為真,require將正常返回。反之,require將通過(guò)拋出IllegalArgumentException來(lái)阻止對(duì)象被構(gòu)造。
添加字段
現(xiàn)在主構(gòu)造器可以正確地執(zhí)行先決條件,我們將把注意力集中到支持加法。想做到這點(diǎn),我們將在類Rational上定義一個(gè)公開(kāi)的add方法,它帶另一個(gè)Rational做參數(shù)。為了保持Rational不可變,add方法必須不能把傳入的分?jǐn)?shù)加到自己身上。而是必須創(chuàng)建并返回一個(gè)全新的帶有累加值的Rational。你或許想你可以這么寫add:
- class Rational(n: Int, d: Int) { // 編譯不過(guò)
- require(d != 0)
- override def toString = n +"/"+ d
- def add(that: Rational): Rational =
- new Rational(n * that.d + that.n * d, d * that.d)
- }
很不幸,上面的代碼會(huì)讓編譯器提示說(shuō):
- < console>:11: error: value d is not a member of Rational
- new Rational(n * that.d + that.n * d, d * that.d)
- ?
- < console>:11: error: value d is not a member of Rational
- new Rational(n * that.d + that.n * d, d * that.d)
- ?
盡管類參數(shù)n和d都在你的add代碼可引用的范圍內(nèi),但是在調(diào)用add的對(duì)象中僅能訪問(wèn)它們的值。因此,當(dāng)你在add的實(shí)現(xiàn)里講n或d的時(shí)候,編譯器將很高興地提供給你這些類參數(shù)的值。但絕對(duì)不會(huì)讓你使用that.n或that.d,因?yàn)閠hat并不指向add被調(diào)用的Rational對(duì)象。實(shí)際上,在that指的是調(diào)用add的對(duì)象時(shí), Rational可以加到自己身上。但是因?yàn)槟憧梢詡鬟f任何Rational對(duì)象給add,所以編譯器仍然不會(huì)讓你說(shuō)that.n。要想訪問(wèn)that的n和d,需要把它們放在字段中。代碼6.1展示了如何把這些字段加入類Rational。
在代碼6.1展示的Rational版本里,我們?cè)黾恿藘蓚€(gè)字段,分別是numer和denom,并用類參數(shù)n和d初始化它們。盡管n和d是用在類的函數(shù)體內(nèi),因?yàn)樗麄冎皇怯迷跇?gòu)造器之內(nèi),Scala編譯器將不會(huì)為它們自動(dòng)構(gòu)造域。所以就這些代碼來(lái)說(shuō),Scala編譯器將產(chǎn)生一個(gè)有兩個(gè)Int域的類,一個(gè)是numer,另一個(gè)是denom。我們還改變了toString和add的實(shí)現(xiàn),讓它們使用字段,而不是類參數(shù)。類Rational的這個(gè)版本能夠編譯通過(guò),可以通過(guò)分?jǐn)?shù)的加法測(cè)試它:
- class Rational(n: Int, d: Int) {
- require(d != 0)
- val numer: Int = n
- val denom: Int = d
- override def toString = numer+"/"+denom
- def add(that: Rational): Rational =
- new Rational(
- numer * that.denom + that.numer * denom,
- denom * that.denom
- )
- }
代碼 6.1 帶字段的Rational
- scala> val oneHalf = new Rational(1, 2)
- oneHalf: Rational = 1/2
- scala> val twoThirds = new Rational(2, 3)
- twoThirds: Rational = 2/3
- scala> oneHalf add twoThirds
- res0: Rational = 7/6
另一件之前不能而現(xiàn)在可以做的事是在對(duì)象外面訪問(wèn)分子和分母。只要訪問(wèn)公共的numer和denom字段即可:
- scala> val r = new Rational(1, 2)
- r: Rational = 1 / 2
- scala> r.numer
- res7: Int = 1
- scala> r.denom
- res8: Int = 2
自指向
關(guān)鍵字this指向當(dāng)前執(zhí)行方法被調(diào)用的對(duì)象實(shí)例,或者如果使用在構(gòu)造器里的話,就是正被構(gòu)建的對(duì)象實(shí)例。例如,我們考慮添加一個(gè)方法,lessThan,來(lái)測(cè)試給定的分?jǐn)?shù)是否小于傳入的參數(shù):
- def lessThan(that: Rational) =
- this.numer * that.denom < that.numer * this.denom
這里,this.numer指向lessThan被調(diào)用的那個(gè)對(duì)象的分子。你也可以去掉this前綴而只是寫numer;著兩種寫法是相同的。
舉一個(gè)不能缺少this的例子,考慮在Rational類里添加max方法返回指定分?jǐn)?shù)和參數(shù)中的較大者:
- def max(that: Rational) =
- if (this.lessThan(that)) that else this
這里,***個(gè)this是冗余的,你寫成(lessThan(that))也是一樣的。但第二個(gè)this表示了當(dāng)測(cè)試為假的時(shí)候的方法的結(jié)果;如果你省略它,就什么都返回不了了。
【相關(guān)閱讀】
- Scala Rational對(duì)象的toString方法
- 學(xué)習(xí)Scala中的Rational類:分?jǐn)?shù)的模型化
- Scala中的富包裝器:富操作和富類列表
- Scala操作符的優(yōu)先級(jí)和關(guān)聯(lián)性
- Scala對(duì)象的相等性比較
文章名稱:在Scala中檢查先決條件、添加字段和自指向
分享地址:http://www.fisionsoft.com.cn/article/cdjjcps.html


咨詢
建站咨詢
