広告

Java : アクセス修飾子の基本

カプセル化と情報隠蔽を実現するために、Javaではアクセス修飾子を使います。
アクセス修飾子(publicprivateなど)を使うと、クラスのメンバをどこまで公開するか?どこまで非公開にするか?を制御できます。

本記事では、そんなアクセス修飾子の基本をご紹介します。


概要

6.6. Access Control
The Java programming language provides mechanisms for access control, to prevent the users of a package or class from depending on unnecessary details of the implementation of that package or class. If access is permitted, then the accessed entity is said to be accessible.

Javaでは、自分で宣言したクラスやインタフェース、そしてそのメンバを

  • どこまで公開するか?
  • どこまで非公開にするか?

という制御が可能となっています。

これをアクセス制御といいます。
また、その制御にアクセス修飾子(publicやprivateなど)を使います。

カプセル化と情報隠蔽

カプセル化(英: encapsulation)は、コンピュータプログラミングで用いられる概念で互いに関連するデータとロジックなどを1つのモジュールとしてまとめることである。また、より広い意味ではまとめたモジュールの内側の詳細を外側から隠蔽することをも含む。

Javaはオブジェクト指向をベースとしたプログラミング言語です。
そして、オブジェクト指向の大事な要素としてカプセル化と情報隠蔽があります。

必要最低限の情報のみを公開して、実装の詳細などは非公開(隠蔽)にするということですね。

カプセル化

こうすることにより、非公開の部分に修正が入っていたとしても、公開部分のみを使っている側には影響させない、という利点があります。
他の言い方をすると、クラス間の依存が小さくなります。(実装の詳細に依存しなくなります)

Javaでは情報隠蔽を実現するためにアクセス修飾子を使います。


アクセス修飾子の一覧と公開範囲

アクセス修飾子の一覧と、その公開範囲の表になります。

アクセス修飾子 同一クラス 別のクラス
(同一パッケージ)
サブクラス
(同一パッケージ)
別のクラス
(別パッケージ)
サブクラス
(別パッケージ)
public
private × × × ×
protected ×
なし (package access) × ×

この中で特に重要なのは、

  • public (公開)
  • private (非公開)

この2つになります。
最低限、この2つを理解できれば、Javaによるプログラミングは可能です。

まずは publicprivate の違いをしっかりと理解していきましょう。

public

public アクセス修飾子は、どこからでも、だれからでもアクセスが可能となります。
もっとも制限のゆるいアクセス修飾子です。

下記の図は、SampleA を中心とした公開範囲のイメージです。

public構成

private

private アクセス修飾子は、自分自身(同一クラス)からのみアクセス可能となります。
もっとも制限の厳しいアクセス修飾子です。

private構成


protected

protected アクセス修飾子は、サブクラスからもアクセスできるのが特徴です。
ただし、同一パッケージ内では public と同じようにアクセスできてしまうのでご注意ください。

protected構成

アクセス修飾子なし (package access)

アクセス修飾子なしは、同一パッケージからのアクセスのみを許可します。
別パッケージからのアクセスはできません。

package accessやpackage privateとも呼ばれています。

package構成


コード例

それぞれのアクセス修飾子の公開範囲は、なんとなくイメージできましたでしょうか。
それでは、実際にアクセス修飾子を使ったコード例を見ていきましょう。

クラス

トップレベルのクラスには、クラスそのものにアクセス修飾子をつけることができます。
ただし、publicアクセス修飾子なし(package access) だけが可能です。(private, protectedは指定できません)

public アクセス修飾子をつけたクラス例です。

package aaa;

public class PublicSample {
}

アクセス修飾子なしのクラス例です。

package aaa;

class PackageSample {
}

同一パッケージの別クラスからアクセスする例です。
どちらもアクセス可能です。

package aaa;

public class MainA {
    public void func() {
        final var publicSample = new PublicSample(); // OK
        final var PackageSample = new PackageSample(); // OK
    }
}

別パッケージの別クラスからアクセスする例です。
public のみアクセス可能です。

package xxx;

import aaa.PackageSample;
import aaa.PublicSample;

public class MainX {
    public void func() {
        final var publicSample = new PublicSample(); // OK
        final var PackageSample = new PackageSample(); // コンパイルエラー
    }
}

補足

  • トップレベルのクラスとは、例えば Sample.java ファイルであれば、Sampleクラスのことをいいます。
    javaファイルで宣言するもっとも外側にあるクラスですね。
  • トップレベル以外のクラスには、メンバクラス(内部クラス)やローカルクラス、匿名クラスがあります。

フィールド

同一クラスからのアクセス例です。
すべてのアクセス修飾子でアクセスが可能です。

package aaa;

public class SampleA {
    public int publicValue;
    private int privateValue;
    protected int protectedValue;
    int packageValue;

    public void func() {
        System.out.println(publicValue); // OK
        System.out.println(privateValue); // OK
        System.out.println(protectedValue); // OK
        System.out.println(packageValue); // OK
    }
}

同一パッケージの別クラスからのアクセス例です。
privateだけがアクセスできません。

package aaa;

public class SampleB {

    public void func() {
        final var sampleA = new SampleA();

        System.out.println(sampleA.publicValue); // OK
        System.out.println(sampleA.privateValue); // コンパイルエラー
        System.out.println(sampleA.protectedValue); // OK
        System.out.println(sampleA.packageValue); // OK
    }
}

同一パッケージのサブクラスからのアクセス例です。
privateだけがアクセスできません。

package aaa;

public class SubSampleA extends SampleA {

    public void func() {
        System.out.println(publicValue); // OK
        System.out.println(privateValue); // コンパイルエラー
        System.out.println(protectedValue); // OK
        System.out.println(packageValue); // OK
    }
}

別パッケージの別クラスからのアクセス例です。
publicのみがアクセス可能です。

package xxx;

import aaa.SampleA;

public class SampleX {

    public void func() {
        final var sampleA = new SampleA();

        System.out.println(sampleA.publicValue); // OK
        System.out.println(sampleA.privateValue); // コンパイルエラー
        System.out.println(sampleA.protectedValue); // コンパイルエラー
        System.out.println(sampleA.packageValue); // コンパイルエラー
    }
}

別パッケージのサブクラスからのアクセス例です。
publicとprotectedのみアクセス可能です。

package xxx;

import aaa.SampleA;

public class SubSampleX extends SampleA {

    public void func() {
        System.out.println(publicValue); // OK
        System.out.println(privateValue); // コンパイルエラー
        System.out.println(protectedValue); // OK
        System.out.println(packageValue); // コンパイルエラー
    }
}

メソッド

基本的な考え方はフィールドと同じです。

同一クラスからのアクセス例です。

package aaa;

public class SampleA {

    public void publicFunc() {
        System.out.println("public!");
    }

    private void privateFunc() {
        System.out.println("private!");
    }

    protected void protectedFunc() {
        System.out.println("protected!");
    }

    void packageFunc() {
        System.out.println("package access!");
    }

    public void func() {
        publicFunc(); // OK
        privateFunc(); // OK
        protectedFunc(); // OK
        packageFunc(); // OK
    }
}

同一パッケージの別クラスからのアクセス例です。

package aaa;

public class SampleB {

    public void func() {
        final var sampleA = new SampleA();

        sampleA.publicFunc(); // OK
        sampleA.privateFunc(); // コンパイルエラー
        sampleA.protectedFunc(); // OK
        sampleA.packageFunc(); // OK
    }
}

同一パッケージのサブクラスからのアクセス例です。

package aaa;

public class SubSampleA extends SampleA {

    public void func() {
        publicFunc(); // OK
        privateFunc(); // コンパイルエラー
        protectedFunc(); // OK
        packageFunc(); // OK
    }
}

別パッケージの別クラスからのアクセス例です。

package xxx;

import aaa.SampleA;

public class SampleX {

    public void func() {
        final var sampleA = new SampleA();

        sampleA.publicFunc(); // OK
        sampleA.privateFunc(); // コンパイルエラー
        sampleA.protectedFunc(); // コンパイルエラー
        sampleA.packageFunc(); // コンパイルエラー
    }
}

別パッケージのサブクラスからのアクセス例です。

package xxx;

import aaa.SampleA;

public class SubSampleX extends SampleA {

    public void func() {
        publicFunc(); // OK
        privateFunc(); // コンパイルエラー
        protectedFunc(); // OK
        packageFunc(); // コンパイルエラー
    }
}

インタフェース

トップレベルのインタフェースには、インタフェースそのものにアクセス修飾子をつけることができます。
ただし、publicアクセス修飾子なし(package access) だけが可能です。(private, protectedは指定できません)

public アクセス修飾子をつけたインタフェース例です。

package aaa;

public interface PublicSample {
}

アクセス修飾子なしのインタフェース例です。

package aaa;

interface PackageSample {
}

同一パッケージの別クラスからアクセスする例です。
どちらもアクセス可能です。

package aaa;

public class MainA {
    public void func() {
        PublicSample publicSample = null; // OK
        PackageSample PackageSample = null; // OK
    }
}

別パッケージの別クラスからアクセスする例です。
public のみアクセス可能です。

package xxx;

import aaa.PackageSample;
import aaa.PublicSample;

public class MainX {
    public void func() {
        PublicSample publicSample = null; // OK
        PackageSample PackageSample = null; // コンパイルエラー
    }
}

フィールド

クラス・フィールドとは違い

  • public
  • アクセス修飾子なし

のみ指定できます。

また、アクセス修飾子なしは public と同等となります。
package access の意味合いではないのでご注意ください。

package aaa;

public interface SampleA {

    // ※valueAとvalueBにアクセス制御の違いはありません。
    public int valueA = 1;
    int valueB = 2;
}

同一パッケージの別クラスからのアクセス例です。

package aaa;

public class SampleB {

    public void func() {
        System.out.println(SampleA.valueA); // OK
        System.out.println(SampleA.valueB); // OK
    }
}

同一パッケージの実装クラスからのアクセス例です。

package aaa;

public class SampleAImpl implements SampleA {

    public void func() {
        System.out.println(valueA); // OK
        System.out.println(valueB); // OK
    }
}

別パッケージの別クラスからのアクセス例です。

package xxx;

import aaa.SampleA;

public class SampleX {

    public void func() {
        System.out.println(SampleA.valueA); // OK
        System.out.println(SampleA.valueB); // OK
    }
}

別パッケージの実装クラスからのアクセス例です。

package xxx;

import aaa.SampleA;

public class SampleXImpl implements SampleA {

    public void func() {
        System.out.println(valueA); // OK
        System.out.println(valueB); // OK
    }
}

メソッド

インタフェース・メソッドは、

  • public
  • private
  • アクセス修飾子なし

を指定できます。

アクセス修飾子なしは public と同等となります。
また、private は必ず実装を含める必要があります。

package aaa;

public interface SampleA {

    // ※ funcA と funcB にアクセス制御の違いはありません。
    public void funcA();

    void funcB();

    // private は実装を含める必要があります。
    private void funcC() {
        System.out.println("C!");
    }
}

同一パッケージで実装(アクセス)する例です。

package aaa;

public class SampleAImpl implements SampleA {

    // OK
    @Override
    public void funcA() {
        System.out.println("A!");
    }

    // OK
    @Override
    public void funcB() {
        System.out.println("B!");
    }

    // コンパイルエラー
    @Override
    public void funcC() {
    }
}

同一パッケージからのアクセス例です。

package aaa;

public class SampleB {

    public void func() {
        final SampleA sampleA = new SampleAImpl();

        sampleA.funcA(); // OK
        sampleA.funcB(); // OK
        sampleA.funcC(); // コンパイルエラー
    }
}

別パッケージで実装(アクセス)する例です。

package xxx;

import aaa.SampleA;

public class SampleXImpl implements SampleA {

    // OK
    @Override
    public void funcA() {
        System.out.println("A!");
    }

    // OK
    @Override
    public void funcB() {
        System.out.println("B!");
    }

    // コンパイルエラー
    @Override
    public void funcC() {
    }
}

別パッケージの別クラスからのアクセス例です。

package xxx;

import aaa.SampleA;

public class SampleX {

    public void func() {
        final SampleA sampleA = new SampleXImpl();

        sampleA.funcA(); // OK
        sampleA.funcB(); // OK
        sampleA.funcC(); // コンパイルエラー
    }
}

まとめ

アクセス修飾子、いかがでしたでしょうか。

プログラミングするさいは、公開範囲を小さく小さくするように意識していきましょう。
基本は private で、必要なところだけ public にしていくのがよいのかなと思います。


関連記事

ページの先頭へ