存取修飾


Scala沒有public關鍵字,在Scala中,沒有使用任何存取修飾的類別成員(不加private也不加protected),就是公開成員(不包括類別參數,類別參數預設是private[this] val),例如:
class Some {
val x = 10 // 預設是公開
def plus = x + 1 // 預設是公開
}

如果定義成員時,使用protected,則表示該成員僅讓繼承後的子類別內部來使用(如果你熟悉Java,請注意,protected在Scala中沒有套件存取的權限):
class Some {
protected val x = 10 // protected 成員
}

class Other extends Some {
def plus = x + 1 // 可在子類別中使用
}

如果定義成員時,使用private,就僅可在類別內部使用,或者同一類別的實例,在類別內部可以透過實例來使用(如果你熟悉Java,請注意,Java中,外部類別可以直接存取內部類別的private成員,但Scala不行),例如:
class Outer {
class Inner {
private val x = 10
def plus = x + 1
}
// 下面這行移除註解會編譯錯誤
// (new Inner).x

private val y = 10
def doMe(o: Outer) = o.y + y // o 在 Outer 中可以直接存取 y
}

protected或private存取修飾可以加上量詞(Qualifier),語法為private[X]或protected[X],X可以是類別、套件或單例物件,這表示權限修飾到X的範圍。

舉個例子來說,下面這個可以通過編譯:
class Some {
private val x = 10
def doSome(s: Some) = s.x + x
}

如果你要更嚴格的修飾,讓x完全無法透過實例存取,則可以使用private[this],這表示私有化至this實例本身才可以存取,也就是所謂物件私有(Object-private),例如以下就通不過編譯了:
class Some {
private[this] val x = 10
def doSome(s: Some) = s.x + x // 編譯錯誤
}

私有化至this實例本身才可以存取,就是說,該成員完全僅可在類別內部使用,所以編譯錯誤的訊息為:
error: value x is not a member of this.Some
    def doSome(s: Some) = s.x + x
                             ^

伴侶類別與伴侶物件預設彼此可以互相存取私有成員,例如:
class Some {
private val x = 10
def minus = Some.y - 1
}

object Some {
private val y = 10
val s = new Some
def plus = s.x + 1
}

但下面這個會編譯錯誤:
class Some {
private[this] val x = 10
def minus = Some.y - 1
}

object Some {
private val y = 10
val s = new Some
def plus = s.x + 1 // 編譯錯誤
}

錯誤訊息如下:
error: value x is not a member of this.Some
    def doSome(s: Some) = s.x + x
                             ^

下面這個也會編譯錯誤:
class Some {
private val x = 10
def minus = Some.y - 1 // 編譯錯誤
}

object Some {
private[this] val y = 10
val s = new Some
def plus = s.x + 1
}

錯誤訊息如下:
error: value y is not a member of object this.Some
   def minus = Some.y - 1
                     ^


在Scala中,protected並沒有套件範圍存取權限,這是因為你可以使用量詞修飾,達到套件存取權限的作用,例如,以下通不過編譯:
package cc.openhome
class Some {
protected val x = 10
}

class Other {
val s = new Some
def get = s.x // 編譯錯誤
}

由於protected沒有套件存取權限,所以編譯錯誤的訊息為:
error: value x cannot be accessed in cc.openhome.Some
    def get = s.x
                ^

但以下就可以編譯成功,因為你使用量詞修飾protected範圍至指定的套件層級,也就是所謂套件私有(Package-private)
package cc.openhome
class Some {
protected[openhome] val x = 10
}

class Other {
val s = new Some
def get = s.x
}

你不僅可以指定至openhome套件,你可以指定任何一層套件,例如:
package cc.openhome
class Some {
protected[cc] val x = 10
}

class Other {
val s = new Some
def get = s.x
}

再看看先前的例子,原先通不過編譯:
class Outer {
class Inner {
private val x = 10
def plus = x + 1
}
(new Inner).x // 編譯錯誤
}

編譯錯誤的訊息為:
error: value x cannot be accessed in Outer.this.Inner
    (new Inner).x
                 ^

你可以指定private防護層級至外部類別,也就是所謂類別私有(Class-private)(讓外部類別可以存取內部類別的private成員,也就是如同Java的作法),例如以下可以通過編譯:
class Outer {
class Inner {
private[Outer] val x = 10
def plus = x + 1
}
(new Inner).x
}

總而言之,private、protected看似可修飾的權限很少,但加上private[X]、protected[X],就可以讓你指定更細部的權限控制。