屬性存取方法


如果你如下定義一個類別:
class Ball {
var radius = 10.0
}

val ball = new Ball
ball.radius = 20.0
println(ball.radius) // 顯示 20.0

乍看之下,這個定義似乎不被鼓勵,因為客戶端可以直接存取radius成員。如果是Java的話,為將radius設為private,並設值方法(Setter)、取值方法(Getter),必要時建立保護條件判斷:
public class Ball {
    private double radius = 10.0;
    public void setRadius(double radius) {
        if(radius < 0) {
            throw new IllegalArgumentException("不能是負數");
        }

        this.radius = radius;
    }
    public double getRadius() {
        return radius;
    }
}

事實上,當你在類別中定義一個非private的var成員時,其實隱含地都會為其建立一對存(Modifier)取(Accessor)方法,也就是設值方法、取值方法,它們用以存取private[this] var的資料成員。例如,先前的Scala所定義的Ball類別,事實上相當於以下的定義方式:
class Ball {
private[this] var r = 10.0
def radius: Double = r // Accessor
def radius_=(a: Double) { r = a } // Modifier
}

val ball = new Ball
ball.radius = 20.0
println(ball.radius) // 顯示 20.0

private [X]中的X是Scala為protected及private所提供的修飾詞(Qualifier),用以提供更細部的可視範圍(Scope)修飾。 private[this]表示所宣告的變數,只能在類別定義中被存取,任何實例化物件時再存取該屬性的動作都不允許,例如:
class Some {
    private[this] var data = "XD"
    def doSome(s: Some) = data + s.data  // 錯誤,不可以使用s.data存取
}

上例中由於data使用private[this]宣告,所以即使是傳入Some的Some實例,也不可以直接存取其data(Java中使用private宣告的成員是可以這麼作的)。

所以若你要在存取Ball物件的radius屬性時,提供保護條件,則可以如下:
class Ball {
private[this] var r = 10.0
def radius: Double = r
def radius_=(a: Double) {
require(a >= 0)
r = a
}
}

val ball = new Ball
ball.radius = 20.0
println(ball.radius) // 顯示 20.0
ball.radius = -1 // 錯誤,IllegalArgumentException: requirement failed
println(ball.radius)

屬性存取方法並不一定得有對應的內部資料成員,也可以是計算的結果。例如:
class Ball {
private[this] var r = 10.0
def radius: Double = r
def radius_=(a: Double) {
require(a >= 0)
r = a
}
def volume: Double = 4.0 / 3.0 * Math.Pi * Math.pow(r, 3)
def volume_=(v: Double) {
require(v >= 0)
r = Math.pow((3.0 * v) / (4.0 * Math.Pi), 1.0 / 3.0)
}
}

val ball = new Ball
ball.radius = 10.0
println(ball.volume) // 顯示 4188.790204786391
ball.volume = 5000
println(ball.radius) // 顯示 10.60784417947055

上例中,實際上並沒有volume對應的內部資料成員,而是將計算對應至r或者是從r計算出volume。