Java道|publicメソッドで安全に操作する方法

中身は守り、使い方は開く。
public メソッドを使えば、クラスは安全で使いやすい部品になる。

前回は、private を使ってクラスのメンバを守る方法を学びました。

private をつけたフィールドは、クラスの外から直接アクセスできなくなります。
そのため、隊士の名前、体力、呼吸の力のような大事な情報を、外から勝手に書き換えられにくくできます。

鬼滅の刃でたとえると、隊士の内部情報に結界を張るようなものです。
誰でも自由に体力や呼吸の力を書き換えられる状態では、隊士として自然な状態を保てません。

ただし、private で守るだけでは、別の問題も出てきます。

外から直接触れないということは、安全ではあります。
しかし、正しい名前や体力、呼吸の力を設定したいときにも、外から何もできなくなってしまいます。

そこで必要になるのが public メソッドです。

フィールドは private で守る。
必要な操作だけ public メソッドとして外に公開する。

この形にすると、クラスの中身を守りながら、安全な使い方だけを外へ見せることができます。

鬼滅の刃でたとえると、隊士の内部記録は結界で守りつつ、正式な報告窓口や訓練手順だけを外に開いておくイメージです。

private だけでは安全でも使いにくい

private は、クラスの外から直接触れないようにするための指定です。

たとえば、DemonSlayer クラスのフィールドを private にすると、次のようなイメージになります。

private String name;
private int stamina;
private double breathingEnergy;

このようにしておけば、main メソッドのようなクラスの外側から、次のような直接代入はできません。

mizuki.name = "水月";
mizuki.stamina = 100;
mizuki.breathingEnergy = -10.0;

これは安全です。

特に、breathingEnergy に -10.0 のような不自然な値を入れられる道を閉じられるので、クラスの中身を守りやすくなります。

しかし、private だけでは困る場面もあります。

状態よい点困る点
フィールドを private にする外から勝手に触れない正しい値も直接設定できない
外から直接代入できない不自然な値を防ぎやすい操作するための入口が必要

鬼滅の刃でたとえると、隊士の情報を結界で完全に守った状態です。
外敵から守れるのはよいですが、正しい管理者まで情報を更新できないと、隊士名簿として使いにくくなります。

そこで、「直接は触れないけれど、決められた方法なら操作できる」という入口を用意します。

それが public メソッドです。

public メソッドは外から使える入口

public をつけたメソッドは、クラスの外から利用できます。

つまり、フィールドは private で隠して守り、操作用のメソッドだけ public で公開します。

この考え方を表で整理すると、次のようになります。

メンバ使い方役割
private フィールド外から直接触らせない内部データを守る
public メソッド外から呼び出せる安全な操作口にする

鬼滅の刃でたとえると、隊士の体力や呼吸の力そのものは、外から直接触れないようにします。
その代わり、正式な訓練手順や報告窓口だけを外に公開します。

たとえば、呼吸の力を設定するときも、外から breathingEnergy に直接値を入れさせるのではありません。
setStatus() のような public メソッドを通して、値が正しいかを確認してから反映します。

これにより、クラスの外からは「安全な入口」だけを使って操作できるようになります。

直接代入ではなく専用メソッドを通す

具体的なプログラムで見てみましょう。

ファイル名:Sample2.java

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

    public void setStatus(String n, int s, double e)
    {
        if (e > 0 && e < 1000) {
            name = n;
            stamina = s;
            breathingEnergy = e;
            System.out.println("隊士の名前を" + name + "に体力を" + stamina + "に呼吸の力を" + breathingEnergy + "にしました。");
        }
        else {
            System.out.println(e + "は正しい呼吸の力ではありません。");
            System.out.println("呼吸の力を変更できませんでした。");
        }
    }

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

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

        // mizuki.name = "水月";
        // mizuki.stamina = 100;
        // mizuki.breathingEnergy = -10.0;

        mizuki.setStatus("水月", 100, 20.5);
        mizuki.show();

        System.out.println("正しくない呼吸の力(-10.0)を指定してみます・・・");

        mizuki.setStatus("水月", 100, -10.0);
        mizuki.show();
    }
}

このプログラムでは、name、stamina、breathingEnergy が private になっています。

private String name;
private int stamina;
private double breathingEnergy;

そのため、main メソッドから直接フィールドへ代入することはできません。

// mizuki.name = "水月";
// mizuki.stamina = 100;
// mizuki.breathingEnergy = -10.0;

かわりに、setStatus() と show() が public になっています。

public void setStatus(String n, int s, double e)

public void show()

つまり、クラスの外からは次の操作だけを安全な入口として使います。

public メソッド役割
setStatus(String n, int s, double e)隊士の状態を設定する
show()隊士の状態を表示する

フィールドへ直接入れるのではなく、public メソッドを通して操作する。
これが、安全なクラス設計の基本です。

setStatus() は値をチェックする入口

Sample2.java で特に大切なのは、setStatus() の中で値をチェックしているところです。

public void setStatus(String n, int s, double e)
{
    if (e > 0 && e < 1000) {
        name = n;
        stamina = s;
        breathingEnergy = e;
        System.out.println("隊士の名前を" + name + "に体力を" + stamina + "に呼吸の力を" + breathingEnergy + "にしました。");
    }
    else {
        System.out.println(e + "は正しい呼吸の力ではありません。");
        System.out.println("呼吸の力を変更できませんでした。");
    }
}

ここでは、呼吸の力 e が 0 より大きく、1000 より小さいときだけ、フィールドへ値を入れています。

if (e > 0 && e < 1000)

この条件を満たす場合だけ、次の代入が実行されます。

name = n;
stamina = s;
breathingEnergy = e;

つまり、正しい範囲の呼吸の力だけを受け入れるようにしています。

一方、e が -10.0 のような不自然な値なら、else 側の処理が実行されます。

System.out.println(e + "は正しい呼吸の力ではありません。");
System.out.println("呼吸の力を変更できませんでした。");

この場合、breathingEnergy は変更されません。

鬼滅の刃でたとえると、隊士の呼吸の力を記録するときに、管理役が「この値は自然な範囲か」を確認してから名簿へ反映しているようなものです。

正しい値なら設定される

main メソッドでは、まず次のように呼び出しています。

mizuki.setStatus("水月", 100, 20.5);

このとき、setStatus() には次の値が渡されます。

実引数仮引数意味
水月n隊士の名前
100s体力
20.5e呼吸の力

呼吸の力 e は 20.5 です。

これは、条件 e > 0 && e < 1000 を満たします。

そのため、次のようにフィールドへ代入されます。

フィールド入る値
name水月
stamina100
breathingEnergy20.5

そして show() を呼ぶと、状態が表示されます。

mizuki.show();

実行結果

隊士の名前を水月に体力を100に呼吸の力を20.5にしました。
隊士の名前は水月です。
体力は100です。
呼吸の力は20.5です。

ここでは、public メソッドを通して正しい値が設定されています。

不正な値なら設定されない

次に、不自然な値を渡しています。

mizuki.setStatus("水月", 100, -10.0);

このとき、呼吸の力 e には -10.0 が入ります。

しかし、-10.0 は条件 e > 0 && e < 1000 を満たしません。

e の値条件 e > 0 && e < 1000結果
20.5true設定される
-10.0false設定されない

そのため、else 側が実行されます。

-10.0は正しい呼吸の力ではありません。
呼吸の力を変更できませんでした。

そして、もう一度 show() を呼びます。

mizuki.show();

このとき、呼吸の力は 20.5 のままです。

実行結果の後半は、次のようになります。

正しくない呼吸の力(-10.0)を指定してみます・・・
-10.0は正しい呼吸の力ではありません。
呼吸の力を変更できませんでした。
隊士の名前は水月です。
体力は100です。
呼吸の力は20.5です。

ここがとても大切です。

もし breathingEnergy に直接代入できる設計なら、-10.0 がそのまま入ってしまう可能性がありました。

しかし、今は setStatus() を通るので、クラスの中でチェックできます。
その結果、不自然な値を内部状態へ入れずにすんでいます。

public メソッドの中で値を守れる

public メソッドを入口にすると、クラス自身が値を確認できます。

外から直接フィールドを触れる設計では、次のような危険な代入を止められません。

mizuki.breathingEnergy = -10.0;

しかし、フィールドを private にし、public メソッドからだけ設定する形にすれば、値を入れる前にチェックできます。

設計値のチェック
フィールドへ直接代入チェックしにくい
public メソッド経由メソッド内でチェックできる

鬼滅の刃でたとえると、呼吸の力を誰でも隊士名簿に直接書ける状態では危険です。
しかし、正式な受付を通して申請する形なら、受付で値を確認できます。

この「受付」が public メソッドです。

Javaのしくみ鬼滅の刃でたとえると
private フィールド結界で守られた隊士の内部記録
public setStatus()正式な状態変更の受付
if 文によるチェック値が自然か確認する審査
show()状態を報告する公開窓口

public メソッドは、ただ外から呼べるだけではありません。
クラスの安全な操作口として、とても大切な役割を持ちます。

private と public は役割分担して使う

private と public は、どちらか一方だけを使うものではありません。

大切なのは、役割分担です。

修飾子役割使いどころ
privateクラスの外から直接触らせないフィールドなどの内部データ
publicクラスの外から使えるようにする操作用のメソッド

基本の考え方は、次の形です。

メンバの種類よくある設計理由
フィールドprivate外から直接壊されないようにする
メソッドpublic決められた操作だけ外に見せる

つまり、

データそのものは守る。
操作方法だけを公開する。

という設計です。

鬼滅の刃でたとえると、隊士の内部状態は隠します。
しかし、状態を整える正式な技や、状態を報告する方法は公開します。

これにより、外から使いやすくしながら、内部状態を守ることができます。

図:private フィールドと public メソッドの役割

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

この図が示していること

この図では、DemonSlayer クラスの内部データを private フィールドとして守り、setStatus() や show() のような public メソッドだけを外へ公開する構造を表しています。

Sample2 の main から private フィールドへ直接向かう赤い矢印は、途中で遮られています。
一方、setStatus() や show() へ向かう青い矢印は通っています。

ここから分かるのは、クラスはすべてを閉じるわけではないということです。
危険な直接操作は閉じ、安全な操作口だけを public メソッドとして開くことで、安全性と使いやすさを両立できます。

カプセル化とは何か

ここで大切な言葉が、カプセル化です。

カプセル化とは、データと機能をひとまとめにし、必要に応じてメンバを保護することです。

Sample2.java の DemonSlayer クラスで見ると、次のようになっています。

種類メンバ役割
データname、stamina、breathingEnergy隊士の状態を持つ
機能setStatus()、show()状態を設定・表示する
保護privateデータを外から直接触らせない
公開public必要な操作だけ外から使わせる

このように、関連するデータと機能をクラスの中にまとめます。
そして、守るべきデータは private にし、外から使わせたい機能だけ public にします。

これがカプセル化の基本です。

鬼滅の刃でたとえると、隊士の内部情報を結界の中に入れて守り、外からは正式な操作口だけを使わせるようなものです。

隊士の内部状態は勝手に触らせない。
でも、状態を整える手順や報告する方法は公開する。

この形にすると、隊士というオブジェクトを安全に扱えるようになります。

カプセル化のよいところ

カプセル化のよいところは、クラスを使う側が危ない操作をしにくくなることです。

たとえば、DemonSlayer クラスを作る人と、それを使う人が別々だったとします。

フィールドが全部外から見えていると、使う人はうっかり次のような値を入れてしまうかもしれません。

mizuki.breathingEnergy = -10.0;

しかし、breathingEnergy が private なら、外から直接は触れません。
使う側は、公開されている setStatus() を通して操作します。

mizuki.setStatus("水月", 100, -10.0);

この場合でも、setStatus() の中でチェックできるので、不自然な値を防ぎやすくなります。

カプセル化していない設計カプセル化した設計
フィールドに直接触れるpublic メソッドを通して操作する
不自然な値が入りやすい値をチェックできる
使い方がばらばらになりやすい使い方を統一できる
変更箇所を追いにくい操作の入口を整理しやすい

鬼滅の刃でたとえると、誰でも隊士名簿へ直接書き込める状態では、情報が壊れやすくなります。
しかし、受付を通して申請する形なら、受付側で不自然な値を止められます。

クラスの仕様側と利用側を分けて考える

クラスを作るときは、仕様を作る側と、それを使う側を分けて考えると分かりやすくなります。

立場役割Sample2.java での場所
クラスの仕様側どんなデータを持ち、どんな操作を許すか決めるDemonSlayer クラス
クラスの利用側公開されたメソッドを使って処理を書くSample2 クラスの main

DemonSlayer クラスは、隊士の仕様を決める側です。

name、stamina、breathingEnergy を持つ。
ただし、それらは private で守る。
外からは setStatus() と show() を使わせる。

このように、使い方のルールをクラス側で決めています。

一方、Sample2 クラスの main は利用側です。

利用側は、直接フィールドを触るのではなく、公開されている public メソッドを使います。

mizuki.setStatus("水月", 100, 20.5);
mizuki.show();

このように、クラスの仕様側が安全な操作を用意しておけば、利用側はその入口に従って書くだけで、自然な使い方がしやすくなります。

これは実際の開発でも大切です。
クラスは、自分だけが使うとは限りません。
あとから別の人が使うこともあります。

だからこそ、クラス側で安全な使い方を用意しておくことが重要です。

修飾子を省略するとどうなるのか

private や public は、アクセスの範囲を決める修飾子です。

修飾子基本の意味
privateクラスの外から直接アクセスできない
publicクラスの外から利用できる

では、何も書かなかった場合はどうなるのでしょうか。

private や public を省略したメンバは、同じパッケージの中からアクセスできる扱いになります。

今の段階では、パッケージの詳しい話はまだ深く考えなくて大丈夫です。
まずは、次の対比をしっかり押さえることが大切です。

書き方今回押さえるポイント
private外から直接触れない
public外から使える
省略同じパッケージ内から使える扱い

最初は、フィールドは private、外から使わせたいメソッドは public という形を意識すると、クラス設計が分かりやすくなります。

図:カプセル化は安全な入口だけを開く考え方

この図が示していること

この図では、カプセル化の考え方を表しています。

DemonSlayer クラスの内部には、name、stamina、breathingEnergy というデータがあり、それらは private で守られています。
外からは、それらへ直接触るのではなく、setStatus() や show() という public メソッドを通して操作します。

setStatus() の入口では、値が自然な範囲かどうかを確認できます。
そのため、-10.0 のような不自然な呼吸の力は、内部データへ反映されません。

ここから分かるのは、カプセル化は単に隠すことではなく、安全な使い方だけを外に見せる設計だということです。

public メソッドを使うとクラスが使いやすくなる

public メソッドは、クラスの外から使える入口です。

ただし、何でも public にすればよいわけではありません。
外から使ってよい操作だけを public にすることが大切です。

Sample2.java では、setStatus() と show() を public にしています。

public メソッド外から使わせる理由
setStatus()正しい手順で状態を設定するため
show()現在の状態を表示するため

このように public メソッドを用意しておくと、利用側はクラスの中身を詳しく知らなくても使えます。

利用側は、次のように呼び出せばよいだけです。

mizuki.setStatus("水月", 100, 20.5);
mizuki.show();

内部では、name、stamina、breathingEnergy に値が入っています。
しかし利用側は、それらへ直接触る必要がありません。

鬼滅の刃でたとえると、隊士の細かい内部記録を知らなくても、正式な窓口へ「名前、体力、呼吸の力」を伝えれば、正しく処理してもらえるようなものです。

public メソッドの中にルールを書ける

public メソッドの強みは、単に外から呼べることだけではありません。

その中に、クラスを守るルールを書けることです。

Sample2.java では、呼吸の力について次のルールを入れています。

if (e > 0 && e < 1000)

これは、

呼吸の力は 0 より大きい
呼吸の力は 1000 より小さい

という条件です。

この条件を満たす場合だけ、breathingEnergy に値を入れます。

このように、public メソッドの中にルールを書くことで、外から渡された値をそのまま信じず、クラス自身が確認できます。

鬼滅の刃でたとえると、隊士の呼吸の力を登録する前に、結界の門番が「この数値は自然か」を確認するイメージです。

public メソッド内の処理意味
値を受け取る外から情報を受け取る
条件でチェックする自然な値か確認する
正しければ代入する内部状態を更新する
不正なら変更しない内部状態を守る

これが、public メソッドで安全に操作する大きな理由です。

いちばん大事な感覚

publicメソッドで安全に操作する方法で大切なのは、次の感覚です。

ポイント内容
private だけでは不足守れるが、外から必要な操作もしづらい
public メソッドを使う外から使える安全な入口を用意する
フィールドは private内部データを直接触らせない
操作は public正しい手順だけ外に公開する
値はメソッド内で確認不自然な値を内部へ入れない
カプセル化データと機能をまとめ、必要なメンバを保護する

鬼滅の刃でたとえると、隊士の大事な内部情報は結界で守ります。
しかし、何もできないように閉じ込めるのではありません。

正式な入口として、状態を設定する setStatus() や、状態を表示する show() を開いておきます。
そして、setStatus() の中で呼吸の力が自然な値かを確認します。

こうすることで、外からは使いやすく、内部は壊れにくいクラスになります。

private で守る。
public で安全な入口を開く。
その入口で値を確認する。

この流れが分かると、クラスはただの情報の箱ではなく、自分の中身を守りながら、外には正しい使い方だけを見せる部品として理解できるようになります。