C++ 的 namespace 和 Java 的 package 有什麼不一樣呢?我的理解是,namespace 只是將一群 class 打包起來,並不涉及 classes 彼此之間的存取權限;但是 package 在 Java 當中是預設的 scope,如果你不為 class member 指定 access modifier 的話,預設就是 package 存取權限 (即在相同 package 之內的所有 classes 都可以存取該成員)
今天在聽同事介紹 Java 繼承的時候,讓我產生了一些困惑。
在 C++ 當中使用繼承時,會在父類別前面加上一個 access modifier,這個 access modifier 定義了父類成員繼承至子類時,所能對外被存取的最大權限 (父類的 public 成員如果用 protected 繼承給子類,那麼子類從父類那邊得到的該成員對外被存取的最大權限就降至 protected)
但是 Java 當中的繼承似乎沒有 access modifier 這件事,就是單純的使用 extends。更令我驚奇的是,父類成員的存取權限到了子類當中竟然還可以變得更開放 (這不是跟 C++ 相反?)
請看以下頭的程式碼中,Base class 有四個不同存取權限的 method,繼承到 Derived 後,凡是將存取權限放寬者皆 OK,將存取權限降低者皆編譯不過。:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Base { | |
private int privMethod() { return 1; } | |
int packMethod() { return 1; } | |
protected int protMethod() { return 1; } | |
public int pubMethod() { return 1; } | |
} | |
class Derived extends Base { | |
//@Override | |
//private int pubMethod() { return 0; } // Error | |
//int pubMethod() { return 0; } // Error | |
//protected int pubMethod() { return 0; } // Error | |
//public int pubMethod() { return 0; } // OK | |
//@Override | |
//private int protMethod() { return 0; } // Error | |
//int protMethod() { return 0; } // Error | |
//protected int protMethod() { return 0; } // OK | |
//public int protMethod() { return 0; } // OK | |
//@Override | |
//private int packMethod() { return 0; } // Error | |
//int packMethod() { return 0; } // OK | |
//protected int packMethod() { return 0; } // OK | |
//public int packMethod() { return 0; } // OK | |
} |
這不禁讓我想到,Java 有這種針對各個成員函式調變對外存取權限的能力,而 C++ 卻只能用 access modifier 定一個最高門檻?於是,Google 了一下竟發現,如果你是用 protected/private 繼承的話,是有辦法把部分因 access modifier 降級的成員函式提昇至 public 的,請看以下程式碼:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
using namespace std; | |
class Base { | |
private: | |
int a; | |
int privMethod() { return a; } | |
public: | |
Base() { a = 0; b = 0;} | |
int b; | |
int pubMethod() { return a; } | |
}; | |
class Derived1 : protected Base { | |
public: | |
Derived1() : Base() {} | |
}; | |
class Derived2 : protected Base { | |
public: | |
Derived2() : Base() {} | |
//Base::a // Error | |
//Base::privMethod; // Error | |
Base::b; | |
Base::pubMethod; | |
}; | |
class Test { | |
public: | |
Derived1 derived1; | |
Derived2 derived2; | |
void test() { | |
//int b1 = derived1.b; // Error | |
//int b2 = derived1.pubMethod(); // Error | |
cout << derived2.b << endl; | |
cout << derived2.pubMethod() << endl; | |
} | |
}; | |
int main() { | |
Test test; | |
test.test(); | |
return 0; | |
} |
在 Test::test() 函式當中可以看到,Derived1 類是用 protected 繼承 Base (改成 private 也可),所以對外是無法存取他的成員的,但是 Derived2 類利用 Base:: 的方式,讓因 protected 繼承而改變存取權限的成員,又成為 public 了。那會想說,可不可以連同把 Base 類當中的 private 成員也變成 public,可以看到在 Derived2 類當中的嘗試,是不行的!
雖然經過實驗大概比較清楚 C++ 和 Java 繼承對於成員物件的影響,但是我依舊不明白,C++ 越繼承對外權限越小,而 Java 越繼承對外權限越大,這兩個相反的設計思考是怎麼樣的考量?
石頭的回答:
這個我直覺的認為是基本上 C++ 跟 Java 的 object model 的差異。在 C++ 中如果父類別的 public method 被子類別改成 private,client 只要轉型成父類別就可以操作 method。在 Java 中所有繼承都是虛擬繼承。
關於 C++ Namespace 和 Java Package 的討論可以參考:
1. C++ Namespaces, comparison to Java packages | Stack Overflow
2. Java packages vs. C++ libraries | Stack Overflow
關於 C++ access modifier 可以參考:
1. inheritance - C++ subclassing access modifier? | Stack Overflow
2. Inheritance: private and protected inheritance
關於 Java 類別成員存取權限可以參考:
1. Controlling Access to Members of a Class