HapInS Developers Blog

HapInSが提供するエンジニアリングの情報サイト

【Java】アクセス修飾子を軽くまとめてみた

はじめに

最近Javaの勉強を始めた者です、どうもこんにちは。
Javaでの業務経験がまだまだ浅いので、JavaSilverの資格を取ろうと現在は参考書片手にコツコツやっているところです。

前置きはさておき、アクセス修飾子の問題だったり解説が多めに振られている印象があったので、軽くまとめていきます。

意外と基礎中の基礎って後から忘れてしまうことがあったりするので、
自分の頭の中を整理するためにも記事に書き起こしたいと思います。

※もっと分かりやすい例が浮かんだり、万が一間違えていた場合が、適宜修正は行う予定です。

アクセス修飾子

なにそれ

以下に対して他のクラスからアクセスを許可するか、否かの指定を行うもの

  • クラス
  • コンストラク
  • メンバ変数
  • メソッド

じゃ、まとめたので終わります





終わりません。



上述したものに対してアクセス修飾子を指定して、
継承したり呼び出したりをするときに、使わせないようにしたり使えるようにしたりと、
実装時に様々な考慮をする必要があります。

公開範囲

各種アクセスの権限だったりを軽く表にまとめると、こんな感じ。

アクセス修飾子 クラス※ コンストラクタ※ メンバ変数※ メソッド※ 説明
public どのクラスでも利用可能
protected × このクラスを継承したサブクラス、もしくは同一パッケージ内のクラスから利用可能
デフォルト(指定なし) 同一パッケージ内のクラスからのみ利用可能
private × 同一クラス内のみからの利用可能

※適用可能な場所(×印がついているところには使えない)
なので、下記のような定義はNGです。

package sample.access;

// コンパイルエラー
protected class Test {
    // 諸々定義
}
package sample.access;

// コンパイルエラー
private class Test {
    // 諸々定義
}


実際に継承したり呼び出したりしたサンプルがあった方が分かりやすい気がするので、
それも軽くまとめていこうと思います。

public/private

こんな感じのクラスを作って、どっかのクラスに呼び出します。
※あくまでも動かす用のサンプルなので、ご了承ください。

public修飾子はどのクラスからもアクセスが可能です
private修飾子に関しては、同一クラスからのみアクセス可能な制限が厳しいものとなっています。

package sample.access;

public class Sample {
    // メンバ変数
    private String name;
    private int id;

    // コンストラクタ
    public Sample(String name, int id) {
        this.name = name;
        this.id = id;
    }

    // public
    public void publicOutput() {
        System.out.println("This is Sample");
    }

    // private
    private void privateOutput() {
        System.out.println("This is Sample2");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

}


上述のクラスを呼び出してみます。

package sample.access.test;

import sample.access.Sample;

public class MainSample {
    public static void main(String[] args) {
        // インスタンス化
        Sample sample = new Sample("test", 10);
        // publicなので呼べる
        sample.publicOutput();
        
        // privateメソッドを、別クラスで呼ぼうとしているので、コンパイルエラーになる
        sample.privateOutput();
    }
}

MainSample.javaの方で、privateメソッドを呼び出そうとしている箇所があるので、
これはルールに従っていないことになるので、コンパイルエラーが発生します。
private修飾子の何かを使いたいときは、あくまでも同一クラス内のみ。

実装時の設計にもよるとは思いますが、
他のクラスに使ってほしくない
そもそもそのクラス内でしか使わない
そういった場合に、考慮していく必要があるということになります。

特に重要な情報や値等を、
簡単に変えられないようにしていくことも必要な場合があります。

protected

お次はprotected修飾子
これは、継承したサブクラスと、同一パッケージ内のクラスから利用可能です。
例えばこんな感じ

package sample.access;

public class SampleProtected {
    protected String name = "aaa";
    protected int id = 12345;
    private String company;
    private int proceeds;

    // コンストラクタ
    public SampleProtected(String companyName) {
        this.company = companyName;
    }

    // protected修飾子のコンストラクタ
    protected SampleProtected(int proceeds) {
        this.proceeds = proceeds;
    }

    // protected修飾子のメソッド
    protected void proctedOutput() {
        System.out.println("protected");
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public int getProceeds() {
        return proceeds;
    }

    public void setProceeds(int proceeds) {
        this.proceeds = proceeds;
    }

}

上述したクラスと同じパッケージに配置されているクラスで呼び出すとこんな感じ

// 同一のパッケージに配置されているクラス
package sample.access;

public class MainSample {
    public static void main(String[] args) {
        // protected修飾子のコンストラクタを呼び出せる
        SampleProtected sample = new SampleProtected(100);
        
        // protected修飾子のメソッドを呼び出せる
        sample.proctedOutput();
    }
}

あとは継承したサブクラスであれば、別のパッケージからも呼び出せると

// 別パッケージ
package sample.access.test;

import sample.access.SampleProtected;

public class AnotherPackage extends SampleProtected {

    public AnotherPackage(int proceeds) {
        super(proceeds);
    }

    public void anotherClassMethod() {
        // 継承したスーパークラスのprotected修飾子にアクセスができる
        System.out.println(super.id);
    }

}

下記のように、継承関係にないクラスかつ
protected修飾子を別パッケージからアクセスしようとするとエラーが起きます。

// 別パッケージ
package sample.access.test;

public class MainSample {
    public static void main(String[] args) {
        // サブクラスをインスタンス化
        AnotherPackage sample = new AnotherPackage(100);

        // スーパークラスを継承したサブクラスからの呼び出しでも、このクラスがスーパークラスとは別パッケージのため、
        // protected修飾子にアクセスできない
        String name = sample.name;
        int id = sample.id;
    }
}
デフォルト(指定なし)

最後!
同一パッケージ内のクラスからのみ利用可能な指定なしさんです。

package sample.access.df;

public class SampleDefault {
    // defaultメンバ変数
    String name = "aaa";
    int id = 1234;
    private String company;

    // defaultメソッド
    SampleDefault(String company) {
        this.company = company;
    }

    void Print() {
        System.out.println("test");
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}

下記のように、同じパッケージであればdefault修飾子で作られたクラスも呼び出したりできます。

// 同一パッケージ
package sample.access.df;

public class SampleCallDefault {
    public static void main(String[] args) {
        // 以下は同一パッケージのため、諸々処理が可能
        SampleDefault sample = new SampleDefault("testCompany");

        int id = sample.id;
        String name = sample.name;

        sample.Print();
    }
}

ただし、別パッケージからdefault修飾子へのアクセスは行えません。

// 別のパッケージに配置されているクラス
package sample.access;

public class MainSample {
    public static void main(String[] args) {
        // この時点でコンパイルエラー
        SampleDefault sample = new SampleDefault();
    }
}

まとめ

今回は本当に初歩中の初歩を簡単にまとめてみましたが、
改めて資格勉強中の自分にとっても、復習と再確認の機会になったかなと思う次第でございます。

某ビジネス本にはこんなニュアンスの言葉が書かれていて、

効率を上げていくためにも、必ず基礎を振り返る時間を作ること

この言葉を忘れず、Javaに限らず何かを学ぶときは、
基礎を徹底して頭に入れる時間は作りたいなと思う次第でございます。

またどこかのタイミングで、こんな感じの初歩中の初歩だろ!
みたいなものを書ければなーと思う次第でございます。
(3回同じ言葉締めてみたかったのでお許しください)

それでは皆様またどこかで