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

戦士の大事な情報は、誰でも自由に書き換えてよいものではありません。メンバを守る考え方を知ると、クラスは安全で信頼できる部品になります。

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

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

ドラゴンボールの世界観でたとえると、サイヤ人戦士という設計図を作り、その中に名前、戦闘力、流派の印、流派名、気の量などの情報を持たせます。さらに、状態を表示する、戦闘力を確認する、気を高める、といった動きもまとめられます。

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

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

もし、悟空の戦闘力や気の量、流派情報を、外から誰でも自由に書き換えられるとしたらどうでしょうか。

戦闘力がマイナスになる。
気の量が不自然な値になる。
流派名が空になる。
戦闘中なのに外から勝手に状態を変えられる。

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

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

そこで必要になるのが、メンバを守るアクセス制御の考え方です。この記事では、クラスのメンバを守る理由を、ドラゴンボールの世界観に置き換えて整理していきます。

クラスのメンバとは何か

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

メンバの種類役割ドラゴンボールでのイメージ
フィールドオブジェクトの情報を持つ戦士の名前、戦闘力、気の量、流派
メソッドオブジェクトの処理や動きを持つ状態表示、戦闘力確認、気の操作
メンバフィールドとメソッドの総称戦士を構成する情報と技

たとえば、サイヤ人戦士を表すクラスを考えると、次のようなメンバがありそうです。

種類意味
フィールドname戦士の名前
フィールドpower戦闘力
フィールドstyleMark流派の印
フィールドstyleName流派名
フィールドkiEnergy気の量
メソッドshow()戦士の状態を表示する

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

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

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

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

流派の情報も大事なメンバになる

この記事では、流派を表す情報として styleMark と styleName を使います。

styleMark は、流派を短く表す印です。
styleName は、流派の正式な名前です。

styleMarkstyleName
亀仙流
鶴仙流
武泰斗流
カリン流
ピッコロ魔族流
神流(ナメック流)
惑星戦士流
北の界王流

たとえば悟空なら、styleMark は 亀、styleName は 亀仙流 です。

この流派情報も、戦士の状態を表す大事なフィールドです。

もし外から自由に書き換えられると、悟空なのに styleMark が 空文字 になったり、styleName がまったく関係ない名前になったりする可能性があります。

つまり、名前や戦闘力だけでなく、流派情報も守る対象になります。

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

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

ファイル名:Sample1.java

class Saiyan
{
    String name;
    int power;
    String styleMark;
    String styleName;
    double kiEnergy;

    void show()
    {
        System.out.println("戦士の名前は" + name + "です。");
        System.out.println("戦闘力は" + power + "です。");
        System.out.println("流派の印は「" + styleMark + "」です。");
        System.out.println("流派名は" + styleName + "です。");
        System.out.println("気の量は" + kiEnergy + "です。");
    }
}

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

        goku.name = "悟空";
        goku.power = 9000;
        goku.styleMark = "亀";
        goku.styleName = "亀仙流";
        goku.kiEnergy = 20.5;

        goku.show();
    }
}

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

goku.name = "悟空";
goku.power = 9000;
goku.styleMark = "亀";
goku.styleName = "亀仙流";
goku.kiEnergy = 20.5;

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

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

goku.show();

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

戦士の名前は悟空です。
戦闘力は9000です。
流派の印は「亀」です。
流派名は亀仙流です。
気の量は20.5です。

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

名前は悟空。
戦闘力は9000。
流派の印は亀。
流派名は亀仙流。
気の量は20.5。

どれも自然な値です。

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

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

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

goku.kiEnergy = 20.5;

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

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

goku.kiEnergy = -10.0;

気の量がマイナスになるのは、戦士の状態としてかなり不自然です。

ドラゴンボールでたとえると、気を高めるサイヤ人戦士の気の量が -10.0 と記録されているようなものです。
これは戦士データとしておかしな状態です。

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

goku.kiEnergy = -10.0;

戦闘力が -50 の悟空というのも、自然ではありません。

さらに、流派情報も壊れてしまう可能性があります。

goku.styleMark = "";
goku.styleName = "";

このように書けてしまうと、悟空がどの流派に属しているのか分からなくなります。

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

ここがとても大切です。

外から直接触れる状態起こりうる問題
kiEnergy に自由に代入できる気の量がマイナスになる
power に自由に代入できる戦闘力が不自然な値になる
name に自由に代入できる空文字や不自然な名前になる
styleMark に自由に代入できる流派の印が壊れる
styleName に自由に代入できる流派名が不自然になる
どこからでも変更できるどこで壊れたのか追いにくくなる

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

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

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

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

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

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

ドラゴンボールでたとえると、Saiyan クラスは、サイヤ人戦士を自然に表すための設計図です。

サイヤ人戦士なら、名前を持つ。
サイヤ人戦士なら、戦闘力を持つ。
サイヤ人戦士なら、流派の印を持つ。
サイヤ人戦士なら、流派名を持つ。
サイヤ人戦士なら、気の量を持つ。
サイヤ人戦士なら、状態を表示できる。

このように、サイヤ人戦士らしい情報と動きをまとめます。

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

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

フィールド不自然な値の例なぜ危ないか
power-50戦闘力として自然ではない
kiEnergy-10.0気の量として自然ではない
name空の文字列誰の情報か分からない
styleMark空の文字列流派の印が分からない
styleName空の文字列流派名が分からない

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

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

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

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

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

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

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

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

ドラゴンボールでたとえると、スカウターの戦士データに間違った戦闘力や気の量が記録され、その情報をもとに戦闘配置を決めてしまうようなものです。

本当は戦える戦士を休ませてしまうかもしれません。
逆に、危険な状態の戦士を強敵の前に出してしまうかもしれません。

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

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

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

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

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

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

ドラゴンボールでたとえると、戦士の情報を誰でも勝手に書き換えられる状態にするのではなく、決められた手順を通して管理するようなものです。

たとえば、ブルマが作った戦士データ管理システムを考えてみましょう。

誰でも戦士データを書き換えられる。
決められた管理画面だけが正しい手順で更新できる。

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

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

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

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

この図が示していること

この図では、Sample1 の main から Saiyan クラスの power や kiEnergy に直接不自然な値を入れている様子を表しています。

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

ここから分かるのは、フィールドを自由に触れる設計は手軽ですが、安全とは限らないということです。

特に、オブジェクトが自然な状態を保つ必要がある場合、外からの直接操作には注意が必要です。

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

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

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

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

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

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

操作外から許してよいか理由
show() を呼ぶ許してよい状態を確認する自然な操作
name を直接書き換える注意が必要不自然な名前になる可能性がある
power に負の値を入れる防ぎたい戦闘力として不自然
kiEnergy に負の値を入れる防ぎたい気の量として不自然
styleName を空にする防ぎたい流派情報が壊れる

ドラゴンボールでたとえると、戦士の状態を確認することは許してもよいです。
しかし、スカウターの戦闘力データや気の量を誰でも勝手に書き換えられるのは危険です。

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

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

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

小さなプログラムなら、フィールドに直接代入しても、すぐに流れを追えるかもしれません。

しかし、プログラムが大きくなると、いろいろな場所から同じオブジェクトが使われます。

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

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

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

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

ドラゴンボールでたとえると、戦士データや修行記録を、誰でも自由に書き換えられる場所に置いておくのは危険です。

決められた管理画面や正しい手順を通して更新するからこそ、情報の信頼性が保たれます。

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

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

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

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

たとえば、Saiyan クラスで考えるなら、show() は外から使わせたいメソッドかもしれません。

戦士の状態を表示する機能は、利用者にとって必要だからです。

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

styleMark や styleName も、直接自由に書き換えさせると流派情報が壊れる可能性があります。

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

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

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

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

以下の条件でイラストを作成してください。

カラーイラスト、16:9横長。白と青を基調にした学習教材風デザイン。ドラゴンボール風のアニメ調。背景はブルマの戦士データ管理室のような未来的な研究施設。青いホログラム、スカウター風UI、保護シールドを配置する。

画面中央に大きな「Saiyan クラス」カードを配置し、内部に次のフィールドを表示する。

「name」
「power」
「styleMark」
「styleName」
「kiEnergy」

これらのフィールドを青い保護シールドで囲み、「守りたい情報」と表示する。

カードの外側に「外部 main」カードを配置する。
外部 main からフィールドへ直接向かう赤い矢印を描き、矢印に「直接変更は危険」と表示する。

別の青い矢印で、外部 main から「show()」へ向かう安全なルートを描き、「決められた操作は許可」と表示する。

下部に「アクセス制御は、必要な操作だけを通して自然な状態を守る」と表示する。

カードの横に小さな悟空アイコンを配置する。悟空アイコンはワインレッドの道着で右胸に「亀」の丸いマークを入れる。髪型と顔、特に目元は原作キャラクターに似すぎないよう、学習教材向けのオリジナル調にする。文字は読みやすく、背景やキャラクターと重ならないようにする。

この図が示していること

この図では、Saiyan クラスの内部にある name、power、styleMark、styleName、kiEnergy が守られている様子を表しています。

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

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

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

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

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

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

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

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

を考えていきます。

ドラゴンボールでたとえると、8章は「サイヤ人戦士を作り、情報や技を持たせる段階」です。
9章は「戦士の大事な情報を守り、正しい手順で扱えるようにする段階」です。

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

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

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

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

こうした点も大切です。

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

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

ドラゴンボールでたとえると、守られていないクラスは、戦士データベースが誰でも自由に書き換えられる状態です。

守られたクラスは、決められた手順でのみ情報を更新できる戦士管理システムです。

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

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

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

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

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

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

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

ドラゴンボールでたとえると、悟空の戦闘力や気の量、流派情報は、誰でも勝手に書き換えてよいものではありません。

本人や管理システムが、決められた手順で確認し、更新するべきものです。

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

図:作って使う設計から、安全に使う設計へ

この図が示していること

この図は、8章で学んだ「クラスを作って使う」段階から、9章で学ぶ「クラスを安全に使う」段階へ進む流れを示しています。

8章では、クラス、オブジェクト、フィールド、メソッド、メンバアクセスを学びました。

9章では、そのメンバを誰がどこまで触ってよいのかを考えます。

ここから分かるのは、Javaのクラスは、ただ情報と処理をまとめるだけではなく、自然な状態を保てるように守って設計するものだということです。

いちばん大事な感覚

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

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

ドラゴンボールでたとえると、戦士の大事な情報を誰でも勝手に書き換えられると危険です。

名前、戦闘力、気の量、流派情報は、戦士の状態そのものです。
それらを自然な形で保つためには、触り方にルールが必要です。

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

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