你定義了一個Ball類別:
class Ball(r: Double) {
def radius = r
}
類別中,radius方法不接受任何參數。在Scala中,不接受任何參數的方法,可以省略()的撰寫,這稱之為無參數方法(Parameterless method),對於僅讀取而不改變物件狀態、且不接受任何參數的方法,在Scala中的慣例會省略括號,表示該方法不會有任何邊際效應(Side effect),相對地,如果不接受任何參數的方法會產生邊際效應,則保留括號的撰寫,稱之為空括號方法(Empty-paren method)。
今天假設有個客戶端使用了你的Ball類別:
val ball = new Ball(10.0)
println(ball.radius) // 顯示 10.0
同樣地,如果操作方法時,該方法如果不會產生邊際效應,則呼叫方法時慣例上也是省略括號。如果哪天你改變了Ball類別之撰寫:
class Ball(val radius: Double)
就剛才那個客戶端的程式碼並不會受到影響,這是 固定存取原則(Uniform access principle) 的簡單例子。固定存取原則指的是,提供客戶端服務時所使用的名稱必須固定,無論該服務是透過計算或既有的結果。就如同上例,客戶端要取得球的半徑,都是透過radius這個名稱來取得,無論是你將radius實作為方法或者是變數。
要注意的是在Scala中,只有值(Value)與型態(Type)兩個名稱空間(Namespace),其中:
- 資料成員(Field)、方法(Method)、套件(Package)與單例(Singleton)物件屬於值(Value)名稱空間。
- 類別(Class)與特徵(Trait)屬於型態屬於型態(Type)名稱空間。
(由於單例物件與類別分屬於值與型態名稱空間,所以才可以相同的名稱形成伴侶。)
由於方法與資料成員屬於同一個名稱空間,所以同一個類別中,資料成員不可以與方法是相同的名稱。例如:
class Ball { // 這個類別會編譯錯誤
private val radius = 10
def radius = 20
}
private val radius = 10
def radius = 20
}
即使是繼承,如果子類別中有與父類別中的成員相同名稱的成員也不行。如果子類別中的方法名稱與父類別的方法名稱相同,則是重新定義方法,你必須使用override關鍵字指定。例如:
class Ball(r: Double) {
def volume = 4 * Math.Pi * Math.pow(r, 3) / 3
override def toString = "radius: " + r +
"\nvolume: " + volume
}
class IronBall(r: Double) extends Ball(r)
上例中還定義了一個IronBall,如果有客戶使用這個IronBall類別:
val ball = new IronBall(10.0)
println(ball.volume)
今天基於某個理由,你改寫了IronBall:
class IronBall(r: Double) extends Ball(r) {
override val volume = 4 * Math.Pi * Math.pow(r, 3) / 3
}
在IronBall中使用val定義了volume這個名稱,由於方法與資料成員屬於同一個名稱空間,如果你確定在子類別中要這麼作,必須重新定義,也就是使用override關鍵字指定,在Scala中,允許你將無參數方法重新定義為val資料成員。對於先前使用IronBall的客戶端而言,並沒有差別,這是固定存取原則的另一個簡單例子。
注意!你可以將父類別的無參數方法在子類別中重新定義為val資料成員,但不能將父類別的val資料成員在子類別中重新定義為無參數方法。例如以下會編譯錯誤:
class Ball(r: Double) {
val volume = 4 * Math.Pi * Math.pow(r, 3) / 3
override def toString = "radius: " + r +
"\nvolume: " + volume
}
class IronBall(r: Double) extends Ball(r) {
// error, method volume is not stable
override def volume = 4 * Math.Pi * Math.pow(r, 3) / 3
}
val volume = 4 * Math.Pi * Math.pow(r, 3) / 3
override def toString = "radius: " + r +
"\nvolume: " + volume
}
class IronBall(r: Double) extends Ball(r) {
// error, method volume is not stable
override def volume = 4 * Math.Pi * Math.pow(r, 3) / 3
}
不穩定的理由在於,原先volume是個val,也就是值固定不變,若能重新定義為方法,則方法的傳回值可能會因計算而不同,如果這個修改允許,你的客戶端在IronBall修改之後,執行的結果就會受到影響(如果IronBall的狀態是可變的話)。