Javaのアクセスレベル (public, protected, パッケージプライベート, private)

ブログを解説してから1ヶ月以上放置してしまいましたが、ようやく初記事です。 ちゃんと定期的に技術ブログできる人、尊敬です。

今回は Effective Javaを片手にJavaのアクセスレベルについて書こうと思います。

Effective Java は1年ほど前に会社の研修で読んだもので、研修中は訳者の柴田芳樹さんに講師として直々に解説をいただきました。

アクセスレベルについては 第4章 クラスとインターフェースの項目15, 16で触れられています。

4つのアクセスレベル

まずは、トップレベルの(ネストしていない)クラスとインターフェースに関するアクセスレベルと、メンバー(フィールド、メソッド、ネストしたクラス、ネストしたインターフェース)に関するアクセスレベルを見ていきます。

トップレベルのクラス、インターフェース

public パッケージプライベート
同一パッケージ内
全ての場所 -

メンバー

public protected パッケージプライベート private
宣言されたトップレベルのクラス内
同一パッケージ内 -
宣言されたクラスのサブクラス内 - -
全ての場所 - - -

public, protected, privateの修飾子をつけるとそれぞれのアクセスレベルになります。

何も修飾子をつけないとパッケージプライベートになります。(ただし、インターフェースのメンバーはデフォルトでpublicです。)

具体例

実際に試してみましょう。

トップレベルのクラス

トップレベルのクラスからです。

package topclass.access1;

// publicなクラス
public class PublicClass {
    public String str = "Hello, world!";
}
package topclass.access1;

// パッケージプライベートなクラス
class PackagePrivateClass {
    public String str = "Hello, world!";
}

publicなクラスと、パッケージプライベートなクラスを作成しました。

続いて、別のパッケージからこれらのクラスをインスタンス化します。

package topclass.access2;

import topclass.access1.PublicClass;

public class AccessPublicClass {
    public static void main(String[] args) {
        PublicClass c = new PublicClass();
        System.out.println(c.str);
        // Hello, world!
    }
}

publicなクラスはアクセスできました。

package topclass.access2;

import topclass.access1.PackagePrivateClass;

public class AccessPackagePrivateClass {
    public static void main(String[] args) {
        PackagePrivateClass c = new PackagePrivateClass();
        // java: topclass.access1のtopclass.access1.PackagePrivateClassはpublicではありません。パッケージ外からはアクセスできません
    }
}

パッケージプライベートなクラスはコンパイルエラーになります。

アクセスするために、同じパッケージでインスタンス化してみましょう。

package topclass.access1;

public class AccessPackagePrivateClass {
    public static void main(String[] args) {
        PackagePrivateClass c = new PackagePrivateClass();
        System.out.println(c.str);
        // Hello, world!
    }
}

アクセスできました。

クラスメンバー

続いて、クラスのメンバーの場合です。

package member.access1;

public class PublicClass {
    public int publicValue = 1;
    protected int protectedValue = 2;
    int packagePrivateValue = 3;
    private int privateValue = 4;
}

それぞれのアクセスレベルをもつメンバ変数を用意しました。

まずは、同一のパッケージからアクセスします。

package member.access1;

public class AccessPublicClass {
    public static void main(String[] args) {
        PublicClass c = new PublicClass();
        System.out.println(c.publicValue); // 1
        System.out.println(c.protectedValue); // 2
        System.out.println(c.packagePrivateValue); // 3
        // System.out.println(c.privateValue);  コンパイルエラー
        // java: privateValueはmember.access1.PublicClassでprivateアクセスされます
    }
}

別のクラスからは、private変数にはアクセスできません。

次に、別のパッケージからアクセスしてみます。

package member.access2;

import member.access1.PublicClass;

public class AccessPublicClass extends PublicClass {
    public static void main(String[] args) {
        PublicClass c = new PublicClass();
        System.out.println(c.publicValue); // 1
        // System.out.println(c.protectedValue);   コンパイルエラー
        // java: protectedValueはmember.access1.PublicClassでprotectedアクセスされます
        // System.out.println(c.packagePrivateValue); コンパイルエラー
        // java: member.access1.PublicClassのpackagePrivateValueはpublicではありません。パッケージ外からはアクセスできません
    }
}

こうすると、public変数のみアクセス可能になります。

では、別パッケージからprotected変数にアクセスしてみます。

package member.access2;

import member.access1.PublicClass;

public class AccessPublicClass2 extends PublicClass {
    public static void main(String[] args) {
        AccessPublicClass2 c = new AccessPublicClass2();
        System.out.println(c.publicValue); // 1
        System.out.println(c.protectedValue); // 2

        PublicClass c2 = new AccessPublicClass2();
        // System.out.println(c2.protectedValue); コンパイルエラー
        // java: protectedValueはmember.access1.PublicClassでprotectedアクセスされます
    }
}

PublicClassを継承したサブクラスを作成し、そのインスタンスを通じてアクセスできます。

ただし、インスタンス変数の型を親クラスとしてしまうと、アクセスできません。

長くなってしまったので今回はここまでとし、次回にこれらのアクセスレベルの使い分けについて書きたいと思います。