Fork me on GitHub

7/29/2011

[Java] Access Modifier in Java and C++

物件導向主要精神之一就是封裝,於是牽涉到了 "public", "protected", "private" 這些存取權限控制的關鍵字。和 C++ 相比 ,Java 當中又多了 package scope 的概念。

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,將存取權限降低者皆編譯不過。:



這不禁讓我想到,Java 有這種針對各個成員函式調變對外存取權限的能力,而 C++ 卻只能用 access modifier 定一個最高門檻?於是,Google 了一下竟發現,如果你是用 protected/private 繼承的話,是有辦法把部分因 access modifier 降級的成員函式提昇至 public 的,請看以下程式碼:



在 Test::test() 函式當中可以看到,Derived1 類是用 protected 繼承 Base (改成 private 也可),所以對外是無法存取他的成員的,但是 Derived2 類利用 Base:: 的方式讓因 protected 繼承而改變存取權限的成員,又成為 public 了。那會想說,可不可以連同把 Base 類當中的 private 成員也變成 public,可以看到在 Derived2 類當中的嘗試,是不行的!

雖然經過實驗大概比較清楚 C++ 和 Java 繼承對於成員物件的影響,但是我依舊不明白,C++ 越繼承對外權限越小,而 Java 越繼承對外權限越大,這兩個相反的設計思考是怎麼樣的考量?

石頭的回答:
這個我直覺的認為是基本上 C++ 跟 Java 的 objec​t 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

No comments:

Post a Comment