在Scala中定義無參數方法
作為下一步,我們將向Element添加顯示寬度和高度的方法,展示在代碼10.2中。height方法返回contents里的行數。width方法返回***行的長度,或如果元素沒有行記錄,返回零。(也就是說你不能定義一個高度為零但寬度不為零的元素。)
- abstract class Element {
- def contents: Array[String]
- def height: Int = contents.length
- def width: Int = if (height == 0) 0 else contents(0).length
- }
代碼 10.2 定義無參數方法width和height
51CTO編輯推薦:Scala編程語言專題
請注意Element的三個方法沒一個有參數列表,甚至連個空列表都沒有。例如,代之以:
方法定義了不加括號的:
- def width(): Int
這種無參數方法在Scala里是非常普通的。相對的,帶有空括號的方法定義,如def height(): Int,被稱為空括號方法:empty-paren method。推薦的慣例是在沒有參數并且方法僅通過讀含有對象的方式訪問可變狀態(專指其不改變可變狀態)時,使用無參數方法。這個慣例支持統一訪問原則:uniform access principle,Meyer,面向對象軟件構造【Mey00】就是說客戶代碼不應受通過字段還是方法實現屬性的決定的影響。例如,我們可以選擇把width和height作為字段而不是方法來實現,只要簡單地在每個實現里把def修改成val即可:
- def width: Int
兩組定義從客戶的觀點來看是完全相同的。唯一的差別是與的訪問或許稍微比方法調用要快,因為字段值在類被初始化的時候被預計算,而方法調用在每次調用的時候都要計算。換句話說,字段在每個Element對象上需要更多的內存空間。因此類的使用概況,屬性表達成字段還是方法更好,決定了其實現,并且這個概況還可以隨時改變。重點是Element類的客戶不應在其內部實現改變的時候受影響。
- abstract class Element {
- def contents: Array[String]
- val height = contents.length
- val width =
- if (height == 0) 0 else contents(0).length
- }
特別是如果類的字段變成了訪問函數,且訪問函數是純的,就是說它沒有副作用并且不依賴于可變狀態,那么類Element的客戶不需要被重寫。客戶都不應該需要關心這些。
目前為止一切良好。但仍然有些瑣碎的復雜的東西要去做以協同Java處理事情的方式。問題在于Java沒有實現統一訪問原則。因此Java里是string.length(),不是string.length(盡管是array.length,不是array.length())。不用說,這讓人很困惑。
為了在這道缺口上架一座橋梁,Scala在遇到混合了無參數和空括號方法的情況時很大度。特別是,你可以用空括號方法重載無參數方法,并且反之亦可。你還可以在調用任何不帶參數的方法時省略空的括號。例如,下面兩行在Scala里都是合法的:
原則上Scala的函數調用中可以省略所有的空括號。然而,在調用的方法表達的超過其接收調用者對象的屬性時,推薦仍然寫一對空的括號。例如,如果方法執行了I/O,或寫入可重新賦值的變量(var),或讀出不是接受調用者的字段的var,無論是直接的還是非直接的通過使用可變對象,那么空括號是合適的。這種方式是讓參數列表扮演一個可見的線索說明某些有趣的計算正通過調用被觸發。例如:
- Array(1, 2, 3).toString
- "abc".length
- "hello".length // 沒有副作用,所以無須()
- println() // ***別省略()
總結起來,Scala里定義不帶參數也沒有副作用的方法為無參數方法,也就是說,省略空的括號,是鼓勵的風格。另一方面,永遠不要定義沒有括號的帶副作用的方法,因為那樣的話方法調用看上去會像選擇一個字段。這樣你的客戶看到了副作用會很奇怪。相同地,當你調用帶副作用的函數,請確信寫這個調用的時候包括了空的括號。另一種考慮這個問題的方式是,如果你調用的函數執行了操作,使用括號,但如果僅提供了對某個屬性的訪問,省略括號。
【相關閱讀】