所謂遮蔽(Shadow),是指在某變數可視範圍內,定義了同名變數,在後者的可視範圍中,取用同一名稱時所用的是後者的定義。例如:
val x = 10
{
val x = 20
println(x) // 顯示 20
}
println(x) // 顯示 10
Scala支援區塊可視範圍,在上例中,{}區塊外宣告了一個x變數,而區塊內也宣告了一個x變數,在區塊中x的變數定義遮蔽了區塊外的x定義。
如果你熟悉Java,你會知道這樣的情況在Java中是允許的:
class A {
protected int x = 10;
}
class B extends A {
public int x = 20; // 這邊 x 遮蔽了 A 類別中的 x
}
public class Main {
public static void main(String[] args) {
B b = new B();
A a = b;
System.out.println(b.x); // 顯示 20
System.out.println(a.x); // 顯示 10
}
}
protected int x = 10;
}
class B extends A {
public int x = 20; // 這邊 x 遮蔽了 A 類別中的 x
}
public class Main {
public static void main(String[] args) {
B b = new B();
A a = b;
System.out.println(b.x); // 顯示 20
System.out.println(a.x); // 顯示 10
}
}
不過有的Java開發人員會誤以為在B中重新定義了x為公開(事實上是遮蔽),而對於最後的顯示結果感到錯愕。在Scala中,繼承時幾乎是不允許遮蔽的,例如,以下的範例會編譯失敗:
class Parent {
protected val x = 10
}
class Child extends Parent {
val x = 20
}
protected val x = 10
}
class Child extends Parent {
val x = 20
}
這樣的情況下,編譯器會認為,你試圖定義一個父類別中已有的成員(也許你不知道這個事實),如果你要這麼作,編譯器會要求你使用override關鍵字,表明你是要重新定義該變數:
error: error overriding value x in class Parent of type Int; value x needs `override' modifier
val x = 20
^
val x = 20
^
以下的程式才可以通過編譯,在Child中,x被重新定義為公開而值設定為20:
class Parent {
protected val x = 10
}
class Child extends Parent {
override val x = 20
}
val c = new Child
println(c.x) // 顯示為 20
在不可視範圍中,沒有遮蔽問題,就不需要使用override關鍵字,例如:
class Parent {
private val x = 10
}
class Child extends Parent {
val x = 20 // 因為 Parent 的 x 在這邊不可視,所以不用 override
}
val c = new Child
println(c.x)
父類別中的成員,在子類別中重新定義時,權限不能縮減,只能重新定義為更寬的權限。例如以下通不過編譯:
class Parent {
val x = 10
}
class Child extends Parent {
override protected val x = 20 // error, value x has weaker access privileges
}
val x = 10
}
class Child extends Parent {
override protected val x = 20 // error, value x has weaker access privileges
}
不過這有個例外,就是private[this],例如以下可以通過編譯:
class Parent {
val x = 10
}
class Child extends Parent {
private[this] val x = 20
def getX = x
}
val c = new Child
println(c.x) // 顯示 10
println(c.getX) // 顯示 20
重 新定義成員時,主要是為了避免影響已使用你程式的客戶端。然而宣告為private[this]的成員,是完全只能在類別中使用,不可能透過參考名稱來存 取該成員,也就是客戶端完全不可能使用到你宣告為private[this]的成員,所以這種情況是允許的,而在這種情況下,例如上例中,Child中的 x在類別中,遮蔽了Parent的x,因此透過getX取回時會是20的值。
另外要注意的是,你不可以重新定義var為val,例如下例通不過編譯:
class Parent {
var x = 10
}
class Child extends Parent {
override val x = 10 // error, value x cannot override a mutable variable
}
var x = 10
}
class Child extends Parent {
override val x = 10 // error, value x cannot override a mutable variable
}
這可以理解,如果x在父類別中是可變動的,若父類別中某些演算方法會改變x的值,在繼承後你將之設為val,原本那些演算方法該怎麼辦呢?