10.1 カプセル化とアクセス修飾子（public、protected、private）～Java Basic編

### はじめに

#### 自己紹介

皆さん、こんにちは、Udemy講師の斉藤賢哉です。私はこれまで、25年以上に渡って企業システムの開発に携わってきました。特にアーキテクトとして、ミッションクリティカルなシステムの技術設計や、Javaフレームワーク開発などの豊富な経験を有しています。
様々なセミナーでの登壇や雑誌への技術記事寄稿の実績があり、また以下のような書籍も執筆しています。
* [「マスタリング Java EE 5」（2007年）](https://amzn.asia/d/j0txqK5)
* [「アプリケーションアーキテクチャ設計パターン」（2017年）](https://amzn.asia/d/9avaCyF)

いずれも**Java EE**（**Jakarta EE**）を中心にした企業システム開発のための書籍です。中でも [「アプリケーションアーキテクチャ設計パターン」](https://amzn.asia/d/9avaCyF)は、（Javaに限定されない）比較的普遍的なテーマを扱っており、内容的にはまだまだ陳腐化していないため、興味のある方は是非手に取っていただけると幸いです（中級者向け）。

#### Udemy講座のご紹介

この記事の内容は、私が講師を務めるUdemy講座『**Java Basic編**』の一部の範囲をカバーしたものです。『**Java Basic編**』はこちらの[**リンク**](https://www.udemy.com/course/learn-java-se-basic/?referralCode=594C958456356851C97C)から購入できます（セールス対象外のためいつも同じ価格）。また**定価の約30%OFFで購入可能なクーポン**をQiita内で定期的に発行していますので、興味のある方は、ぜひ私の他の記事をチェックしてみてください。

この講座は、以下のような皆様にお薦めします。
* Javaの言語仕様や文法を正しく理解すると同時に、現場での実践的なスキル習得を目指している方
* 新卒でIT企業に入社、またはIT部門に配属になった、新米システムエンジニアの方
* 長年IT部門で活躍されてきた中堅層の方で、学び直し（リスキル）に挑戦しようとしている方
* 今後、フリーランスエンジニアとしてのキャリアを検討している方
* 「**Chat GPT**」のエンジニアリングへの活用に興味のある方
* 「**Oracle認定Javaプログラマ**」の資格取得を目指している方
* IT企業やIT部門の教育研修部門において、新人研修やリスキルのためのオンライン教材をお探しの方

#### この記事を含むシリーズ全体像

この記事はJava SEの一部の機能・仕様を取り上げたものですが、一連のシリーズになっており、**シリーズ全体でJava SEを網羅**しています。また認定資格である「**Oracle認定Javaプログラマ**」（Silver、Gold）の範囲もカバーしています。シリーズの全体像および「Oracle認定Javaプログラマ」の範囲との対応関係については、以下を参照ください。

https://qiita.com/KenyaSaitoh/items/4711a078a851510bc4da

## 10.1 カプセル化とアクセス修飾子

### チャプターの概要

このチャプターでは、アクセス修飾子と、それによって実現される「カプセル化」の概念について学びます。

### 10.1.1 アクセス修飾子

#### アクセス修飾子とは

アクセス修飾子とは、クラスやそのメンバーに対する「可視性」、言い方を替えると「公開範囲」を指定するための修飾子です。アクセス修飾子にはpublic、protected、privateの3種類がありますが、「アクセス修飾子を付与しない状態」も可視性の一種と考えられるため、4つに分類するものとします。
それぞれのアクセス修飾子ごとの公開範囲や、そのメンバーに対してアクセス可能な要素を以下の表に示します。

|公開範囲|修飾子|アクセス可能な要素(from what?)|
|--|--|--|
|広い|public|すべてのクラスからアクセス可能|
||protected|自クラスおよび同一パッケージのクラスと、 自身の子クラスからアクセス可能|
||なし|自クラスおよび同一パッケージのクラスからアクセス可能<br>※換言すると、異なるパッケージからはアクセス不可|
|狭い|private|自クラスのみからアクセス可能<br>※換言すると、他クラスからはアクセス不可|

公開範囲は、publicが最も広く、protected、「アクセス修飾子なし」、privateの順に狭まります。「アクセス修飾子なし」の状態よりも、protectedを付与した方が（直感とは異なり）可視性の範囲は広くなりますので、注意してください。
まずpublicが付与された要素に対しては、すべてのクラスからアクセスが可能です（すべてのクラスに公開される）。
次にprotectedが付与された要素に対しては、自クラスおよび同一パッケージのクラスからしかアクセスができません。ただし自クラスを継承（チャプター11.1参照）した子クラスに限っては、異なるパッケージであってもアクセスが可能です。
「アクセス修飾子なし」の要素に対しては、自クラスおよび同一パッケージのクラスからしかアクセスができません。
privateが付与された要素に対しては、他クラスからは一切アクセスができません。

#### アクセス修飾子を付与可能な要素

ここでは、要素ごとに付与することが可能なアクセス修飾子を整理します。
まずクラスに対して付与可能なアクセス修飾子は、publicのみです。一方、クラスのメンバーであるフィールド、メソッド、コンストラクタには、すべての修飾子が付与可能です。

|修飾子|付与可能な要素|
|--|--|
|クラス|publicのみ|
|フィールド|public、protected、private|
|メソッド|public、protected、private|
|コンストラクタ|public、protected、private|

クラスに対するアクセス修飾子と、メンバーに対するアクセス修飾子が競合する場合は、可視性が狭い方が優先されます。つまり例えばクラスが「アクセス修飾子なし」の場合は、メンバーのアクセス修飾子がpublicであったとしても、「アクセス修飾子なし」の可視性が優先されます。（クラスにアクセスできない時点で、必然的にメンバーにもアクセスできない）
以上から、クラスにはpublicを付与し、メンバー毎の可視性の制御はメンバー単位に行う、というのが基本的な方針になります。

#### アクセス修飾子の具体例

ここでは、具体例をもとにアクセス修飾子の仕様を説明します。
以下のように、異なるパッケージに所属するFooクラスとBarクラスがあるものとします。
* pro.kensait.pkg1.Foo
* pro.kensait.pkg2.Bar

Fooクラスのコードを次に示します。

```java:pro.kensait.pkg1.Foo
package pro.kensait.pkg1;
public class Foo {
    private int x;
    public Foo(int x) {
        this.x = x;
    }
    public int getX() {
        return x;
    }
    int pow() {
        return x * x;
    }
}
```

このようにFooクラスでは、まずクラスそのものにpublicを付与しています。メンバーについては、xフィールドにはprivateを、コンストラクタとgetX()メソッドにはpublicを付与しています。またpow()メソッドには、アクセス修飾子を付与していません。
このFooクラスに対して、パッケージが異なるBarクラス内からのアクセスは、以下のようになります。

```java:snippet (pro.kensait.pkg2.Bar)
Foo foo = new Foo(10); //【1】OK
int val1 = foo.x; //【2】NG
int val2 = foo.getX(); //【3】OK
int val3 = foo.pow(); //【4】NG
```

まずFooクラスそのものにpublicを付与しているので、基本的にこのクラスはすべてのクラスに公開されます。またコンストラクタにもpublicを付与しているので、すべてのクラスで、このクラスのインスタンスを生成可能です。従って【1】のようにインスタンスを生成可能です。
xフィールドにはprivateを付与しているので、Barクラスからxフィールドにはアクセスできません（不可視）。従って【2】のようにBarクラス内で`foo.x`と記述すると、コンパイルエラーになります。
getX()メソッドにはpublicを付与しているので、すべてのクラスからアクセス可能です。従って【3】のようにgetX()メソッドは呼び出し可能です。
pow()メソッドは「アクセス修飾子なし」なので、異なるパッケージからはアクセスできません（不可視）。従って【4】のようにBarクラス内でpow()メソッドを呼び出すと、コンパイルエラーになります。

### 10.1.2 カプセル化

#### カプセル化とは

Javaでは、アクセス修飾子によってクラスが保持する属性（フィールド）を外部から隠蔽したり、ローカル変数によってデータをメソッド内に閉じ込めたりすることができます。このようにオブジェクト指向型言語において、クラスに実装された処理の独立性を高めることを「カプセル化」と呼びます。従来の手続き型プログラミング言語では、グローバル変数が引き起こす「スパゲッティコード」が課題でしたが、Javaではカプセル化によって、処理の流れや構造が分かりやすいコードを記述することが可能になります。

#### アクセス修飾子とカプセル化の戦略

プログラムは、開発者自身のためだけに作成するとは限りません。クラスがリポジトリにコミットされ、プロジェクトチーム内で共有され始めると、そのクラスを「他の開発者にどのように公開するか」「どのように使われるのか」を意識しなければなりません。このような点を意識しないと、当該クラスを後で修正したとき、思わぬところで不具合が発生してしまう可能性があるためです。
そのためには各メンバーに対する公開範囲を必要以上に広げすぎないよう、適切にアクセス修飾子を付与したり、カプセル化によって内部情報を隠蔽したりすることが必要です。また詳細はチャプター11.1で触れますが、finalキーワードによって、クラスとメソッドの継承やオーバーライドを制御することも重要です。

#### カプセル化とアクセサメソッド

ここでは、カプセル化を実現するための一般的な方法について説明します。
まずフィールドにpublicを付与することは、原則として回避します。このようにする理由は、フィールドはあくまでもクラスが内部的に保持する属性なので、外部から自由に参照したり更新したりすることができると、カプセル化が崩れてしまうためです。よってフィールドにはprivateを付与し、外部からは不可視にします。
では外部からフィールド値を参照したり更新したりする場合は、どのようにしたらよいのでしょうか。それはアクセサメソッドと呼ばれるメソッドを経由して行います。アクセサメソッドとはフィールドにアクセスするためのメソッドで、ゲッターとセッターの2種類があります。ゲッターとはフィールド値を参照するためのメソッド、セッターとは更新するためのメソッドですが、外部からのアクセスを可能にするためにアクセス修飾子はpublicにします。
アクセサメソッドは、例えばnameフィールドが対象の場合、ゲッターは"getName"、セッターは"setName"というネーミング規約（get、set直後の文字は大文字）に従う必要があります。ここでゲッターおよびセッターから、get、setを取り除き、先頭を小文字化したもの（先の例では"name"）を、プロパティと呼びます。基本的にはフィールド名とプロパティ名は一致させますが、nameフィールドを参照するためのゲッターをgetFullName()メソッドにする（この場合プロパティは"fullName"）ことも技術的には可能です。

#### カプセル化の具体例

ここでは、「人物」を表すPersonクラス（レッスン7.1.2参照）を、カプセル化するケースを考えてみましょう。
以下のコードを見てください。

```java:pro.kensait.java.lsn_10_1_1.Person
public class Person {
    // フィールド
    private String name; // 名前
    private int age; // 年齢
    // コンストラクタ
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // アクセサメソッド
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
```

このようにフィールドはprivateにし、フィールド値を参照・更新するためのアクセサメソッドはpublicにするのが、カプセル化の基本パターンです。
ここでこのPersonクラスのように、ゲッターとセッターが、単純にフィールド値を参照したり更新したりするだけであれば、フィールドをpublicにしても同じなのではないか、と感じるかもしれませんが、それはその通りです。
ただしアクセサメソッドがあることによって、フィールドに対する外部からのアクセスをコントロール可能になるため、以下のようなことを実現可能です。
* 参照専用フィールドを作る。publicなゲッターのみを宣言（セッターなし）すれば、コンストラクタで初期値を代入後、フィールドを参照のみ可能（更新不可）にすることができる。
* フィールドが後から仕様変更になった場合の影響を極小化する。例えばフルネームを表すnameフィールドが、後から苗字（lastNameフィールド）と名前（fisrtNameフィールド）に分割されたケースを考える。nameフィールドを不可視にさえしておけば、このような仕様変更があっても、公開されたgetName()メソッドの内容を「苗字と名前を文字列連結して返す」ように修正するだけで済むため、外部への影響は極小化できる。
* フィールドをセッターを通して更新させることで、必要に応じて、設定される値の有効性を検証する。例えば「年齢（age）の有効範囲は0以上」という要件がある場合は、セッターの中で有効性を検証を行い、設定される値がマイナスの場合はエラーにする。なおこのようなチェックを「事前条件」と呼ぶ[^1]。



### このチャプターで学んだこと

このチャプターでは、以下のことを学びました。

1. アクセス修飾子とは公開範囲を指定するための修飾子であり、public、protected、指定なし、privateの4段階があること。
1. アクセス修飾子は、クラス、フィールド、メソッド、コンストラクタに付与可能であること。
1. カプセル化とは、クラスに実装された処理の独立性を高めること。
1. アクセサメソッドにはゲッター、セッターがあり、カプセル化を実現するためのものであること。

[^1]:事前条件では、if文でメソッド引数の有効性を検証し、エラーの場合はIllegalArgumentException例外を送出するのが一般的（チャプター19.2参照）。ただし実際のJavaアプリケーションでは、有効性の検証ロジックは比較的複雑になったり、ユーザーにエラーメッセージ返却が必要になったりするケースが多いため、セッターで値を設定するより前段階で、別の方法で検証が行われるケースが大半。