Java : アクセス修飾子の基本
カプセル化と情報隠蔽を実現するために、Javaではアクセス修飾子を使います。
アクセス修飾子(publicやprivateなど)を使うと、クラスのメンバをどこまで公開するか?どこまで非公開にするか?を制御できます。
本記事では、そんなアクセス修飾子の基本をご紹介します。
概要
Javaでは、自分で宣言したクラスやインタフェース、そしてそのメンバを
- どこまで公開するか?
- どこまで非公開にするか?
という制御が可能となっています。
これをアクセス制御といいます。
また、その制御にアクセス修飾子(publicやprivateなど)を使います。
カプセル化と情報隠蔽
Javaはオブジェクト指向をベースとしたプログラミング言語です。
そして、オブジェクト指向の大事な要素としてカプセル化と情報隠蔽があります。
必要最低限の情報のみを公開して、実装の詳細などは非公開(隠蔽)にするということですね。
こうすることにより、非公開の部分に修正が入っていたとしても、公開部分のみを使っている側には影響させない、という利点があります。
他の言い方をすると、クラス間の依存が小さくなります。(実装の詳細に依存しなくなります)
Javaでは情報隠蔽を実現するためにアクセス修飾子を使います。
アクセス修飾子の一覧と公開範囲
アクセス修飾子の一覧と、その公開範囲の表になります。
アクセス修飾子 | 同一クラス | 別のクラス (同一パッケージ) |
サブクラス (同一パッケージ) |
別のクラス (別パッケージ) |
サブクラス (別パッケージ) |
---|---|---|---|---|---|
public | 〇 | 〇 | 〇 | 〇 | 〇 |
private | 〇 | × | × | × | × |
protected | 〇 | 〇 | 〇 | × | 〇 |
なし (package access) | 〇 | 〇 | 〇 | × | × |
この中で特に重要なのは、
- public (公開)
- private (非公開)
この2つになります。
最低限、この2つを理解できれば、Javaによるプログラミングは可能です。
まずは public と private の違いをしっかりと理解していきましょう。
public
public アクセス修飾子は、どこからでも、だれからでもアクセスが可能となります。
もっとも制限のゆるいアクセス修飾子です。
下記の図は、SampleA を中心とした公開範囲のイメージです。
private
private アクセス修飾子は、自分自身(同一クラス)からのみアクセス可能となります。
もっとも制限の厳しいアクセス修飾子です。
protected
protected アクセス修飾子は、サブクラスからもアクセスできるのが特徴です。
ただし、同一パッケージ内では public と同じようにアクセスできてしまうのでご注意ください。
アクセス修飾子なし (package access)
アクセス修飾子なしは、同一パッケージからのアクセスのみを許可します。
別パッケージからのアクセスはできません。
package accessやpackage privateとも呼ばれています。
コード例
それぞれのアクセス修飾子の公開範囲は、なんとなくイメージできましたでしょうか。
それでは、実際にアクセス修飾子を使ったコード例を見ていきましょう。
クラス
トップレベルのクラスには、クラスそのものにアクセス修飾子をつけることができます。
ただし、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 にしていくのがよいのかなと思います。
関連記事
- if文の基本
- while文の基本
- for文の基本
- プリミティブ型 (基本データ型)
- リテラルの表記方法いろいろ
- クラスの必要最低限を学ぶ
- インタフェースの default メソッドとは
- インタフェースの static メソッドの使いどころ
- var (型推論) のガイドライン
- 配列 (Array) の使い方
- 拡張for文 (for-eachループ文)
- switch文ではなくswitch式を使おう
- try-with-resources文でリソースを自動的に解放
- テキストブロックの基本
- 列挙型 (enum) の基本
- ラムダ式の基本
- レコードクラスの基本 (Record Class)
- メソッド参照の基本
- シールクラスの基本(Sealed Class)
- 無名変数の使い方