From Gossip@Openhome

Java Gossip: 被保護的(protected)成員

在之前您的資料成員都預設為"private"成員,也就是私用成員,私用成員只能在類別物件中使用,不能直接透過物件來呼叫使用,而即使是擴充了該類別的衍生類別也是如此,您只能透過該類別所提供的"public"方法成員來呼叫或設定私用成員。

然而有些時候,您希望擴充了基底類別的衍生類別,能夠直接存取呼叫基底類別中的成員,但不是透過"public"方法成員,也不是將它宣告為"public",因為您仍不希望這些成員被物件直接呼叫使用。

可以宣告這些成員為「被保護的成員」(protected),保護的意思表示存取它有條件限制以保護該成員,當您將類別成員宣告為受保護的成員之後,繼承它的類別中就可以直接使用這些成員,但這些成員仍然受到物件範圍的保護,不可被物件直接呼叫使用。

要宣告一個成員成為受保護的成員,就使用"protected"關鍵字,下面這個程式是個實際的例子,您將資料成員宣告為受保護的成員,擴充它的類別中就可以直接使用,而不用透過"public"方法成員來呼叫:

  • Rectangle.java
public class Rectangle { 
// 受保護的member
protected int x, y;
protected int width, height;

public Rectangle() {
x = y = 0;
width = height = 0;
}

public Rectangle(int x, int y, int width, int height) {
this.x = x; this.y = y;
this.width = width; this.height = height;
}

public int getX() { return x; }
public int getY() { return y; }
public int getWidth() { return width; }
public int getHeight() { return height; }
public int getArea() { return width*height; }
}
  • Cubic.java
public class Cubic extends Rectangle { 
protected int z;
protected int length;

public Cubic() {
z = 0; length = 0;
}

public Cubic(int x, int y, int z,
int length, int width, int height) {
super(x, y, width, height);
this.z = z;
this.length = length;
}

public int getLength() { return length; }
public int getVolumn() { return length*width*height; }
}
  • UseProtected.java
public class UseProtected { 
public static void main(String[] args) {
Cubic c1 = new Cubic(0, 0, 0, 10, 20, 30);

System.out.println("c1 volumn: " + c1.getVolumn());
}
}

執行結果:
 c1 volumn: 6000

在這個例子中,您可以看到直接使用繼承下來的受保護成員確實比較方便,方法成員也可以宣告為受保護的成員,對象通常是僅適用於 類別中使用的一些內部處理方法,這些方法對類別外部來說,可能是呼叫它並沒有意義或是有危險性,但您在衍生類別中仍可能使用到這些方法,所以通常會將之 宣告為受保護的成員。

一個例子就是在視窗事件處理時,設定事件發生時的呼叫函式,它可以被繼承,以自訂一些視窗元件,這些方法在事件發生時才會被呼叫,您在類 別外直接呼叫這些方法並沒有意義,甚至某些條件沒有成立就呼叫它,對整個程式的執行會造成錯誤,這些方法成員通常就會宣告為受保護的成員。

在設計上有一個考量,就是對物件內部的field成員最好不要直接呼叫,而仍然透過方法呼叫,例如下面的方式有時不被鼓勵:
public class SomeClass {
     private int someInt;
 
     .....

     public void someMethod() {
         int i = someInt;
         .....
         someInt = 1234;
         ....
     }
}

直接在someMethod()中使用field成員,有時會使得someMethod()失去一些彈性,有時建議使用這樣的方式:
public class SomeClass {
     private int someInt;
 
     .....
 
     private int getSomeInt() {
         return someInt;
     }
 
     private void setSomeInt(int someInt) {
         this.someInt = someInt;
     }
 
     public void someMethod() {
         int i = getSomeInt();
         .....
         setSomeInt(1234);
         ....
     }
}

這樣作的好處是,您可以在存取field成員前作一些額外的處理(這些處理可能原先您要在someMethod()中進行),當然何時要使用,以及 setter、getter要宣告為"public
、"protected"或是"private",則視您的程式需求而定了。

事實上,對於同一個 套件(package) 下的類別,可以直接呼叫彼此的protected成員,而對於不同套件(package)下的成員,不能呼叫彼此的protected成員。

如果在定義成員時沒有設定任何的存取修飾,則為預設(default)的存取權限,預設存取權限可以在同一個套件(package)中的其它類別直接存取。