はじめに
自己紹介
皆さん、こんにちは、Udemy講師の斉藤賢哉です。私はこれまで、約30年にわたって企業システムの開発に携わってきました。現在も現役のアーキテクトとして、重要システムの技術設計やソリューションの選定を担当しています。直近では、システム開発へのAI適用(AI駆動開発)という全社的な施策でリーダーを務めています。
セミナーでの登壇や雑誌への技術記事の寄稿を行ってきたほか、以下の書籍も執筆しています。
- 「マスタリング Java EE 5」(翔泳社、2007年)
- 「アプリケーションアーキテクチャ設計パターン」(技術評論社、2017年)
- 「Javaエンジニアのためのソフトウェアテスト実践入門」(技術評論社、2024年)
Udemy講座のご紹介
この記事の内容は、私が講師を務めるUdemy講座『新 Java Basic編 / AI対応版』の一部の範囲をカバーしたものです。『新 Java Basic編 / AI対応版』はこちらのリンクから購入できます。
この講座は、以下のような皆様にお薦めします。
- AIが書いたJavaのコードを読んで、正しさと設計の良し悪しを判断できるようになりたい方
- 新人のシステムエンジニアとして、Javaの基礎を仕組みから身に付けたい方
- AIを前提とした開発へ学び直し(リスキリング)をしたい方
- 「Oracle認定Javaプログラマ」の取得を目指し、Javaの基礎を固めたい方
AIとチャットできるオールインワンの学習環境をリリースしました
Udemy講座の演習を動かすための学習環境として、Windows向けのデスクトップアプリ「Codinable(コーディナブル)」をリリースしました。
エディタ、実行結果、テスト結果、SQL、Webブラウザ、そしてAIチャットを1つの画面にまとめた、オールインワンの学習環境です。
学習に必要なものは、すべてCodinableの中で完結します。
- 演習の実行環境(Java 25、Gradle、Bashなど)が一式同梱されており、JDKのインストールや環境変数の設定は不要です
- インストールして「新規コース開始・再開」からコースを選ぶだけで、講座のすべての演習がそのまま動きます
- 書き換えて動かなくなっても、「初期化」で配布時の状態に戻せます
- 開いている演習のファイルと実行結果を踏まえて、AIに質問したり(Askモード)、ファイルの書き換えを指示したり(Agentモード)できます
AIに書き換えを指示し、差分を確かめてから実行する、というAI駆動開発の基本の流れを、Codinableの中だけで安全に体験できます。
AIチャットは、受講者ご自身が生成AIサービス(Anthropic、OpenAI、Googleのいずれか)のAPIキーを登録して使うBYOK(Bring Your Own Key)方式です。利用料金は各社から直接請求されます。APIキーを登録しなくても、AIチャット以外の機能はすべて使えます。
Codinableのインストーラは、以下のGitHubのReleasesページからダウンロードできます(64ビット版のWindows 10/11、管理者権限は不要)。
この記事を含むシリーズ全体像
この記事はJava SEの一部の機能・仕様を取り上げたものですが、一連のシリーズになっており、シリーズ全体でJava SEを網羅しています。また認定資格である「Oracle認定Javaプログラマ」(Silver、Gold)の範囲もカバーしています。シリーズの全体像および「Oracle認定Javaプログラマ」の範囲との対応関係については、以下を参照ください。
10.1 カプセル化とアクセス修飾子
チャプターの概要
チャプター9.1では、パッケージによってクラスを整理する方法を取り上げました。本チャプターでは、公開範囲を指定するアクセス修飾子と、それを使ってクラスの内部を隠すカプセル化を取り上げます。カプセル化が変更の影響をどう小さくするのかを、アクセサメソッド(ゲッターとセッター)の例で確かめ、AIが書いたクラスの公開範囲が必要以上に広くないかを判断できるようにします。
10.1.1 アクセス修飾子と公開範囲
本レッスンでは、4つの公開範囲と、それがコンパイル時に守られる仕組みを理解します。
4つの公開範囲
アクセス修飾子はpublic、protected、privateの3つと、何も付けない状態(パッケージプライベート)の4段階です。
【表】アクセス修飾子ごとの公開範囲
| 公開範囲 | 修飾子 | アクセスできるクラス |
|---|---|---|
| 広い | public |
すべてのクラス |
| ↑ | protected |
自クラス、同一パッケージのクラス、自クラスの子クラス |
| ↓ | なし | 自クラス、同一パッケージのクラス |
| 狭い | private |
自クラスのみ |
アクセス修飾子なしよりもprotectedの方が広い点に注意してください(子クラスはチャプター11.1参照)。
4つの公開範囲を、パッケージと子クラスの関係で表すと次のようになります。
-
privateの要素は、クラスFoo(自クラス)からだけアクセスできる - アクセス修飾子なしの要素は、自クラスと、同じパッケージ
pkg1のクラスからアクセスできる -
protectedの要素は、それに加えて、別のパッケージpkg2にあるFooの子クラスからもアクセスできる -
publicの要素は、pkg2の無関係なクラスを含む、すべてのクラスからアクセスできる
トップレベルのクラスに付けられるのはpublicか「なし」だけです。クラスとメンバーの公開範囲が食い違うときは狭い方が効き、クラスがアクセス修飾子なしなら、メンバーがpublicでも別のパッケージからは使えません。
コンパイラが守る公開範囲
pro.kensait.pkg1.Fooは、privateのフィールドx、publicのコンストラクタとgetX()、アクセス修飾子なしのpow()を持ちます。別のパッケージのBarからFooにアクセスするコードを以下に示します。
- 10_class_mechanism: pro.kensait.pkg2.Bar
package pro.kensait.pkg2;
import pro.kensait.pkg1.Foo;
public class Bar {
void doSomething() {
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
}
}
- 【1】と【3】は、コンストラクタと
getX()がpublicなので使えます。 - 【2】は
xがprivate、【4】はpow()がアクセス修飾子なしで別のパッケージから見えないため、コメントを外すとコンパイルエラーになります。
Barから見たFooの各メンバーの可否を図で示します。
- 左側は
pro.kensait.pkg2パッケージのBarクラス、右側はpro.kensait.pkg1パッケージのFooクラスである -
Fooの中には、public Foo(int x)、public int getX()、int pow()(修飾子なし)、private int xの4つのメンバーがある -
BarからFoo(int x)とgetX()への矢印には「OK」の印が付く -
Barからpow()とxへの矢印には「コンパイルエラー」の印が付き、パッケージの境界で止まる
公開範囲は実行前にコンパイラが検査し、Codinableではエディターの診断にメッセージが表示されます。
レビューの観点
- フィールドが
publicやprotectedになっていないか。原則としてprivateになっているかを確かめる - 他のクラスから使われる予定の無いメソッドやコンストラクタが
publicになっていないか - アクセス修飾子なしと
protectedの広さを取り違えた設計になっていないか
10.1.2 カプセル化とアクセサメソッド
本レッスンでは、カプセル化が変更の影響をどう小さくするのかを理解し、アクセサメソッドが「形だけ」になっていないかを判断できるようにします。
なぜ内部を隠すのか
カプセル化とは、データとそれを扱う処理をクラスにまとめ、外から触れられる範囲を限定することです。フィールドをprivateにしてメソッドを通してだけ読み書きさせると、書き換える場所がクラスの中に限られます。フィールドを公開した場合と隠した場合を比較します。
- 左側は問題のある設計で、
Personのpublic int ageを、3つの利用側クラスが直接読み書きしている - 左側では、
ageに-1を代入する利用側があっても、Personは止められない - 右側は望ましい設計で、
Personのprivate int ageには、getAge()とsetAge(int)を通してだけアクセスできる - 右側では、
setAge(int)が値を確かめる関所になり、書き換えの経路が1つにまとまる
アクセサメソッド
privateなフィールドを読み書きするpublicなメソッドをアクセサメソッドと呼び、ゲッター(getName()、boolean型はisActive()も可)とセッター(setName())があります。getやsetを除いた名前(name)をプロパティと呼び、フィールド名と一致させるのが基本です。
- 10_class_mechanism: pro.kensait.java.basic.lsn_10_1_1.Person
public class Person {
private String name;
private int age;
// コンストラクタは省略
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// getAge()とsetAge()は省略
}
-
privateのnameとageには、外部からpublicのゲッターとセッターを通してアクセスします。
フィールドとアクセサメソッドの関係を図で示します。
-
Personクラスのフィールドnameとageはprivateであり、利用側クラスから直接アクセスできない -
Personクラスは、publicのアクセサメソッドとしてgetName()・setName(String)・getAge()・setAge(int)を持つ - 利用側クラスは、ゲッターで値を参照し、セッターで値を更新する
カプセル化の効果
アクセサメソッドを通すと、ゲッターだけの参照専用のフィールドにできる、セッターで値を検証できる(例 ageは0以上。例外はチャプター19.1参照)、内部の作りを変えても呼び出し側に影響しない、という3つの効果があります。nameをlastNameとfirstNameに分ける変更を例に、3つ目を図で示します。
- 左側は変更前で、
Personはprivate String nameを持ち、getName()はnameを返す - 右側は変更後で、
Personはprivate String lastNameとprivate String firstNameを持ち、getName()は2つを連結して返す - 両側の上に、同じ利用側クラスがあり、どちらにも
p.getName()と書かれている - 変更されたのは
Personの内部だけで、利用側クラスのコードは変わらない
ただし、すべてのフィールドに機械的にゲッターとセッターを付けると、publicフィールドとほとんど変わりません。AIはアクセサメソッドをまとめて生成しがちなので、必要性を確かめます。
レビューの観点
- すべてのフィールドに、必要の無いセッターまで機械的に生成されていないか
- セッターやコンストラクタで、業務上の制約(範囲、必須など)を検証しているかを確かめる
- ゲッターが内部の配列や変更できるオブジェクトをそのまま返し、外から中身を書き換えられる状態になっていないか
- プロパティ名とフィールド名がずれ、読み手を混乱させていないか
このチャプターで学んだこと
本チャプターでは、以下を取り上げました。
- アクセス修飾子には
public、protected、なし、privateの4つの公開範囲があり、コンパイラによって守られること - クラスとメンバーの公開範囲が食い違うときは狭い方が効き、フィールドは原則として
privateにすること - カプセル化はデータと処理をクラスにまとめて外から触れる範囲を限定することで、書き換えの経路を1つにまとめること
- アクセサメソッドによって、参照専用のフィールド、値の検証、内部の変更の影響の遮断が実現できること
- ゲッターとセッターを機械的に付けたり、内部の配列をそのまま返したりすると、カプセル化が崩れること






