Java道|クラスのメンバを守る方法

隊士の大事な情報は、誰でも自由に触ってよいわけではない。
メンバを守るしくみを知ると、クラスは安全で信頼できる部品になる。

8章では、クラスの基本として、設計図を作り、その設計図からオブジェクトを生み出し、フィールドやメソッドを使って情報や処理を扱う流れを学びました。

クラスを使うと、情報と処理をひとまとまりにできます。

たとえば、鬼滅の刃でたとえると、鬼殺隊士という設計図を作り、その中に名前、体力、呼吸の種類などの情報を持たせます。さらに、状態を表示する、任務へ向かう、体力を確認する、といった動きもまとめられます。

ただし、クラスの本当の強みは、情報と処理をまとめることだけではありません。

大切なのは、クラスの中にある情報を安全に守ることです。

もし、隊士の体力や階級、呼吸の状態を、外から誰でも自由に書き換えられるとしたらどうでしょうか。

体力がマイナスになる
本来ありえない階級になる
呼吸の状態が不自然になる
任務中なのに外から勝手に状態を変えられる

このようなことが起きると、隊士として自然な状態を保てなくなります。

Javaのクラスでも同じです。
フィールドやメソッドを外から自由に触れる状態にしておくと、便利な反面、危ない使われ方をされる可能性があります。

そこで必要になるのが、メンバを守るアクセス制御の考え方です。

クラスのメンバとは何か

クラスの中には、フィールドとメソッドがあります。
この2つをまとめてメンバと呼びます。

メンバの種類役割鬼滅の刃でたとえると
フィールドオブジェクトの情報を持つ隊士の名前、体力、気力、呼吸
メソッドオブジェクトの処理や動きを持つ状態表示、体力確認、任務報告
メンバフィールドとメソッドの総称隊士を構成する情報と技

たとえば、鬼殺隊士を表すクラスを考えると、次のようなメンバがありそうです。

種類意味
フィールドname隊士の名前
フィールドstamina体力
フィールドbreathingEnergy呼吸の力
メソッドshow()隊士の状態を表示する

フィールドは、隊士の状態を保存する場所です。
メソッドは、その隊士に何かをさせる処理です。

8章では、オブジェクトを作り、ドットを使ってフィールドやメソッドにアクセスする方法を学びました。

たとえば、隊士オブジェクトの名前を設定したり、状態を表示したりできました。

これはとても便利です。
しかし、便利だからといって、すべての情報を外から自由に触れる状態にしておくと問題が起きます。

外から自由に触れると危ない理由

まずは、フィールドに外から直接値を入れる形を見てみましょう。

ファイル名:Sample1.java

class DemonSlayer
{
    String name;
    int stamina;
    double breathingEnergy;

    void show()
    {
        System.out.println("隊士の名前は" + name + "です。");
        System.out.println("体力は" + stamina + "です。");
        System.out.println("呼吸の力は" + breathingEnergy + "です。");
    }
}

class Sample1
{
    public static void main(String[] args)
    {
        DemonSlayer mizuki = new DemonSlayer();

        mizuki.name = "水月";
        mizuki.stamina = 100;
        mizuki.breathingEnergy = 20.5;

        mizuki.show();
    }
}

このプログラムでは、DemonSlayer クラスの外にある main メソッドから、フィールドに直接値を入れています。

mizuki.name = "水月";
mizuki.stamina = 100;
mizuki.breathingEnergy = 20.5;

この書き方では、mizuki オブジェクトの中にある name、stamina、breathingEnergy に外から直接アクセスしています。

そのあとで show() を呼び出しています。

mizuki.show();

この場合、出力は次のようになります。

隊士の名前は水月です。
体力は100です。
呼吸の力は20.5です。

ここだけを見ると、特に問題はなさそうです。

名前は水月。
体力は100。
呼吸の力は20.5。

どれも自然な値です。

しかし、問題は「正しい値を入れられる」ことではありません。
危ないのは「不自然な値も入れられてしまう」ことです。

一見便利な直接代入が、危ない入口になる

先ほどの Sample1.java では、breathingEnergy に 20.5 を入れていました。

mizuki.breathingEnergy = 20.5;

これは自然な値に見えます。

では、次のように書けてしまったらどうでしょうか。

mizuki.breathingEnergy = -10.0;

呼吸の力がマイナスになるのは、隊士の状態としてかなり不自然です。

鬼滅の刃でたとえると、全集中の呼吸を使う隊士の呼吸の力が -10.0 と記録されているようなものです。
これは現実の隊士管理としてもおかしな状態です。

同じように、体力も不自然な値になってしまう可能性があります。

mizuki.stamina = -50;

体力が -50 の隊士というのも、自然ではありません。

もちろん、プログラムとして代入できる場合があります。
しかし、代入できることと、正しい状態であることは別です。

ここがとても大切です。

外から直接触れる状態起こりうる問題
breathingEnergy に自由に代入できる呼吸の力がマイナスになる
stamina に自由に代入できる体力が不自然な値になる
name に自由に代入できる空文字や不自然な名前になる
どこからでも変更できるどこで壊れたのか追いにくくなる

小さなプログラムでは、変な値が入ってもすぐに気づけるかもしれません。

しかし、プログラムが大きくなると、どこで不自然な値が入ったのかを見つけるのが難しくなります。

だからこそ、クラスのメンバを守る考え方が必要になります。

クラスは自然な状態を保つための設計図

クラスは、ただ変数をまとめた箱ではありません。

オブジェクト指向では、クラスは「もの」を自然に扱うための設計図です。

鬼滅の刃でたとえると、DemonSlayer クラスは、鬼殺隊士を自然に表すための設計図です。

隊士なら、名前を持つ。
隊士なら、体力を持つ。
隊士なら、呼吸の力を持つ。
隊士なら、状態を表示できる。

このように、鬼殺隊士らしい情報と動きをまとめます。

でも、外からフィールドを自由に書き換えられると、鬼殺隊士らしさが壊れてしまいます。

たとえば、次のような状態は不自然です。

フィールド不自然な値の例なぜ危ないか
stamina-50体力として自然ではない
breathingEnergy-10.0呼吸の力として自然ではない
name空の文字列誰の情報か分からない

クラスは、自然な状態を保ちながら使えるように設計する必要があります。

つまり、クラスの役割は「情報を持つこと」だけではありません。
「不自然な状態にならないように守ること」も大切な役割です。

なぜ不自然な操作を防ぐ必要があるのか

「少しくらい変な値が入っても、あとで直せばいい」と感じるかもしれません。

しかし、プログラムでは、不自然な値が一度入ると、その値が別の処理へどんどん広がることがあります。

たとえば、breathingEnergy が -10.0 のまま別の処理で使われたとします。

すると、次のような問題が起きるかもしれません。

問題内容
表示がおかしくなる呼吸の力は-10.0です。と表示される
計算がおかしくなる呼吸の力を使った計算結果が不自然になる
条件分岐がおかしくなる出撃可能かどうかの判定が狂う
原因を探しにくいどこで不自然な値が入ったのか分からなくなる

鬼滅の刃でたとえると、隊士名簿に間違った体力や呼吸の力が記録され、その情報をもとに任務の配置を決めてしまうようなものです。

体力が足りない隊士を危険な任務に出してしまうかもしれません。
逆に、出撃できる隊士を休ませてしまうかもしれません。

プログラムでも同じで、不自然な値は後続の処理に影響します。

だから、クラスの中にある大事な情報は、外から自由に触れないように守る必要があります。

アクセス制御は勝手に触らせないための考え方

ここで出てくるのが、アクセス制御です。

アクセス制御とは、クラスのメンバに対して、どこから触れるかを制限する考え方です。

外から自由に触ってよいメンバもあります。
外から直接触らせないほうがよいメンバもあります。

鬼滅の刃でたとえると、隊士の情報を誰でも勝手に書き換えられる状態にするのではなく、決められた手順を通して管理するようなものです。

たとえば、鬼殺隊の隊士名簿を考えてみましょう。

誰でも名簿を書き換えられる
決められた管理者だけが正しい手順で更新できる

この2つでは、安全性がまったく違います。

設計のしかた特徴
外から直接フィールドを触れる手軽だが、不自然な値が入りやすい
決められた方法を通して扱う少し手順は増えるが、安全な状態を保ちやすい

アクセス制御は、意地悪に隠すためのものではありません。
クラスを安全で信頼できる部品にするための守りのしくみです。

図:外から直接フィールドを触る危うさ

↓クリックすると拡大表示されます。

この図が示していること

この図では、Sample1 の main から DemonSlayer クラスの breathingEnergy に直接 -10.0 を入れている様子を表しています。

外からフィールドへ直接代入できる状態だと、呼吸の力がマイナスになるような不自然な状態を作れてしまいます。

ここから分かるのは、フィールドを自由に触れる設計は手軽ですが、安全とは限らないということです。
特に、オブジェクトが自然な状態を保つ必要がある場合、外からの直接操作には注意が必要です。

必要な操作だけを許すという考え方

アクセス制御で大切なのは、すべてを閉じ込めることではありません。

必要な操作はできるようにする。
危ない操作はできないようにする。

このバランスが大切です。

たとえば、隊士の状態を表示する show() は外から使えてもよさそうです。
なぜなら、状態を確認することは自然な操作だからです。

一方で、breathingEnergy に -10.0 を直接入れる操作は危険です。
そのような操作は、できないようにしたいところです。

操作外から許してよいか理由
show() を呼ぶ許してよい状態を確認する自然な操作
name を直接書き換える注意が必要不自然な名前になる可能性がある
stamina に負の値を入れる防ぎたい体力として不自然
breathingEnergy に負の値を入れる防ぎたい呼吸の力として不自然

鬼滅の刃でたとえると、隊士の状態を確認することは許してもよいです。
しかし、隊士名簿の体力や呼吸の力を誰でも勝手に書き換えられるのは危険です。

だから、直接触るのではなく、決められた手順を通す設計が必要になります。

クラスを安全な部品として育てる

クラスは、プログラムの中で何度も使われる部品です。

小さなプログラムなら、フィールドに直接代入しても、すぐに流れを追えるかもしれません。
しかし、プログラムが大きくなると、いろいろな場所から同じオブジェクトが使われます。

そのとき、どこからでもフィールドを書き換えられる設計だと、次のような不安が出てきます。

誰が値を変えたのか分からない
いつ不自然な値になったのか分からない
どの処理が原因なのか追いにくい
修正したつもりでも別の場所で壊される

これでは、安心してクラスを使えません。

クラスを安全な部品として使うには、内部の大事な情報を守り、正しい方法で扱えるようにする必要があります。

鬼滅の刃でたとえると、鬼殺隊の隊士名簿や任務記録を、誰でも自由に書き換えられる場所に置いておくのは危険です。
決められた担当者や正しい手順を通して更新するからこそ、情報の信頼性が保たれます。

Javaのクラスでも同じです。

アクセス制御はクラスの使い道を整理する

アクセス制御を考えるときは、次の2つを分けて考えると分かりやすいです。

観点考えること
外から使わせたいもの利用者に公開してよい操作
外から勝手に触らせたくないものクラス内部で守りたい情報

たとえば、DemonSlayer クラスで考えるなら、show() は外から使わせたいメソッドかもしれません。
隊士の状態を表示する機能は、利用者にとって必要だからです。

一方で、stamina や breathingEnergy は直接触らせたくない情報かもしれません。
不自然な値が入ると、隊士の状態が壊れてしまうからです。

このように、クラスの中にあるメンバを見ながら、

これは外から使わせる
これは内部で守る
これは決められた方法でだけ変更する

と整理していくことが、アクセス制御の入口です。

図:守りながら使うクラス設計

↓クリックすると拡大表示されます。

この図が示していること

この図では、DemonSlayer クラスの内部にある name、stamina、breathingEnergy が守られている様子を表しています。

外から直接フィールドへ触る操作は危険な矢印として示し、show() のような決められた操作は安全な矢印として示しています。

ここから分かるのは、アクセス制御は単に隠すためのものではなく、必要な操作だけを通して、クラスの自然な状態を守るための考え方だということです。

8章の基本から9章の守る設計へ進む

8章では、クラスを作って使う基本を学びました。

8章で学んだこと内容
クラスオブジェクトを作る設計図
オブジェクトクラスから作られる実体
フィールドオブジェクトが持つ情報
メソッドオブジェクトが持つ処理
メンバアクセスドットを使ってメンバにアクセスする

ここまでで、クラスの作り方と使い方は見えてきました。

9章では、そこから一歩進んで、

そのメンバに誰が触れてよいのか
どのメンバを外から使わせるのか
どの情報を守るべきなのか

を考えていきます。

鬼滅の刃でたとえると、8章は「隊士を作り、情報や技を持たせる段階」です。
9章は「隊士の大事な情報を守り、正しい手順で扱えるようにする段階」です。

つまり、8章が「作って使う」なら、9章は「安全に使う」ための学習です。

メンバを守ると、クラスは信頼できる部品になる

クラスは、ただ動けばよいわけではありません。

安全に使えること。
不自然な状態になりにくいこと。
使い方が整理されていること。
問題が起きたときに原因を追いやすいこと。

こうした点も大切です。

メンバを守る考え方があると、クラスは信頼できる部品になります。

守られていないクラス守られたクラス
外から自由に値を変えられる決められた方法で扱える
不自然な値が入りやすい不自然な状態を防ぎやすい
原因を追いにくい変更箇所を整理しやすい
使い方がばらばらになりやすい使い方を統一しやすい

鬼滅の刃でたとえると、守られていないクラスは、隊士名簿が誰でも自由に書き換えられる状態です。
守られたクラスは、決められた手順でのみ情報を更新できる隊士管理の仕組みです。

どちらが安心して使えるかは、はっきりしています。

アクセス制御で意識したいこと

アクセス制御を学ぶときは、最初から難しく考えすぎなくて大丈夫です。

まずは、次の感覚を持つことが大切です。

見るポイント意識すること
フィールド外から直接書き換えられてよいか
メソッド外から使わせたい処理か
値の自然さ不自然な値が入る可能性はないか
クラスの責任その情報を誰が管理するべきか

特にフィールドは、オブジェクトの状態を表す大切な情報です。

だからこそ、外から自由に触らせるのではなく、クラス自身が責任を持って管理するという考え方が重要になります。

鬼滅の刃でたとえると、隊士の体力や呼吸の状態は、誰でも勝手に書き換えてよいものではありません。
本人や管理役が、決められた手順で確認し、更新するべきものです。

Javaのクラスでも、同じように「大事な状態はクラス自身に管理させる」と考えると、アクセス制御の意味がつかみやすくなります。

いちばん大事な感覚

クラスのメンバを守る方法で大切なのは、次の感覚です。

ポイント内容
メンバは便利フィールドやメソッドを通して情報と処理を扱える
自由すぎると危ない不自然な値が入る可能性がある
クラスは自然な状態を守るオブジェクトらしい状態を保つことが大切
アクセス制御は守りのしくみ誰がどこまで触れるかを制限する
必要な操作は公開する使いやすさも残す
危ない操作は防ぐ安全で信頼できる部品にする

鬼滅の刃でたとえると、隊士の大事な情報を誰でも勝手に書き換えられると危険です。

名前、体力、呼吸の力といった情報は、隊士の状態そのものです。
それらを自然な形で保つためには、触り方にルールが必要です。

そのルールを作る考え方が、メンバを守るアクセス制御です。

この考え方が分かると、Javaのクラスは単なる情報の箱ではなく、安全に使える部品として設計するものだと見えてきます。