アクセス修飾子 クラスとインスタンスで扱いは同じ?

アクセス修飾子はクラス間のアクセスを制御する仕組みだす。
しかし、クラスとインスタンスを同じに考えてクラスに対する制限がそのままインスタンスに適用されると理解していると思わぬ失敗をしてしまいます。クラスはクラス、インスタンスはインスタンスなのです。

まず、UML図で違いを見てみましょう。
下図のようにスーパークラスを継承しているサブクラスのインスタンスを作るって考えてみます。

インスタンスの作成

図に示すようChild01のインスタンスにはChild01がParent01を継承しているので、Parent01の持っているgetmsg群のメソッドとChild01が持っているgetChildmsg群のメソッドがあります。
Parent01の持っているアクセス修飾子なしのgetmsg3メソッドはサブクラスのChild01からはアクセスできない事はアクセス修飾子 それぞれの違いで説明しましたから、Main01Child01はgetmsg3メソッド以外のインスタンスに含まれるメソッドはアクセスできると考えても仕方がないかもしれません。

しかし、実際のアクセスは下図のような結果になります。

アクセス可能範囲

まず、同じパッケージに属するChild01が持つgetChildmsg群のメソッドは全てMain01Child01からアクセス出来ます。しかし、Child01のスーパークラスParent01が持つメソッドについては、アクセス修飾子なしのgetmsg3メソッドがアクセス出来ないのは明らかとしても、サブクラスからアクセスできるはずのprotected指定されたgetmsg2メソッドもアクセスできません。これはアクセスがサブクラスからではなくサブクラスのインスタンスからだからです。
インスタンスは元になったクラスがサブクラスの場合、サブクラスとそのスーパークラスの仕様・特徴(フィールとメソッド)を持ちますが、スーパークラスのメソッドが持っているアクセス修飾子protectedの意味が変わるわけではありません。
そこで、アクセス修飾子がpublicの時以外は個々にメソッドの所有者に指定されたアクセス修飾子の影響範囲を判断する必要があります。

それでは、具体的なコードで確認しましょう。
まず、スーパークラスのParent01.javaです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ex01.parent;
 
public class Parent01 {
 
    public void getmsg1() {
        System.out.println("親クラスでもpublicなメソッドはどこからでも呼べる");
    }
    protected void getmsg2(String msg) {
        System.out.println(msg);
    }
    void getmsg3(String msg1, String msg2) {
        System.out.println(msg1 + msg2);
    }
}

サブクラスのChild01.javaです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package ex01;
import ex01.parent.Parent01;
 
public class Child01 extends Parent01 {
    public Child01() {
        super.getmsg1();
        super.getmsg2("別パッケージ内の親クラスのprotectedメソッドは子クラスから呼べる");
//      super.getmsg3("別パッケージ内の親クラスのアクセス修飾子なしのメソッドは", "子クラスから呼べばない");
    }
     
    public void getChildmsg1() {
        System.out.println("インスタンス内のpublicなメソッドはどこからでも呼べる");
    }
    protected void getChildmsg2(String msg) {
        System.out.println(msg);
    }
    void getChildmsg3(String msg1, String msg2) {
        System.out.println(msg1 + msg2);
    }
 
}

ここで、8行目の別パッケージ内の親クラスのアクセス修飾子なしのメソッドgetmsg3の呼び出しはアクセス修飾子 それぞれの違いで確認したようにコンパイルエラーになるのでコメントアウトしています。

Child01のインスタンスを作り、実行するMain01Child01.javaです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package ex01;
 
public class Main01Child01 {
    public static void main(String args[]) {
        Child01 cd = new Child01();
         
        System.out.println("---------- インスタンスからのアクセス ----------");
        cd.getmsg1();
        cd.getmsg2("別パッケージ内の親クラスのprotectedメソッドは子クラスのインスタンスから呼べない");
        cd.getmsg3("別パッケージ内の親クラスのアクセス修飾子なしのメソッドは", "子クラスのインスタンスから呼べばない");
         
        cd.getChildmsg1();
        cd.getChildmsg2("子クラスの持つprotectedメソッドは子クラスのインスタンスから呼べる");
        cd.getChildmsg3("子クラスの持つアクセス修飾子なしのメソッドは", "子クラスのインスタンスから呼べる");
    }
}

Main01Child01.javaをコンパイルすると、

C:\javaCode>javac ex01\Main01Child01.java
ex01\Main01Child01.java:9: エラー: getmsg2(String)はParent01でprotectedアクセス されます
                cd.getmsg2("別パッケージ内の親クラスのprotectedメソッドは子クラ スのインスタンスから呼べない");
                  ^
ex01\Main01Child01.java:10: エラー: シンボルを見つけられません
                cd.getmsg3("別パッケージ内の親クラスのアクセス修飾子なしのメソッドは", "子クラスのインスタンスから呼べばない");
                  ^
  シンボル:   メソッド getmsg3(String,String)
  場所: タイプChild01の変数 cd
エラー2個
 
C:\javaCode>

となり、別パッケージ内の親クラスのprotected及びアクセス修飾子なしのメソッドは子クラスのインスタンスから呼べない事が分かります。特にprotected指定のメソッドについてはクラスからと、インスタンスからとではアクセス修飾子の影響が変わったように見えるので注意が必要です。

そこで、9行目と10行目をコメントアウトしてコンパイルし、実行すると、

C:\javaCode>java ex01.Main01Child01
親クラスでもpublicなメソッドはどこからでも呼べる
別パッケージ内の親クラスのprotectedメソッドは子クラスから呼べる
---------- インスタンスからのアクセス ----------
親クラスでもpublicなメソッドはどこからでも呼べる
インスタンス内のpublicなメソッドはどこからでも呼べる
子クラスの持つprotectedメソッドは子クラスのインスタンスから呼べる
子クラスの持つアクセス修飾子なしのメソッドは子クラスのインスタンスから呼べる
 
C:\javaCode>

となり、前半UML図で確認したインスタンスからアクセスする場合にアクセス可能なメソッドが明確になります。

カテゴリー: Java タグ: , , , パーマリンク