Java道|toString()の役割とオーバーライド

オブジェクトは、そのままでは少し無口です。
toString() をオーバーライドすると、オブジェクト自身に「自分の情報を分かりやすく名乗らせる」ことができます。

Javaでは、オブジェクトを画面に表示したい場面がよくあります。

たとえば、作成したオブジェクトにどんな値が入っているのかを確認したいときです。
また、処理の途中でオブジェクトの状態を見たいときにも、System.out.println() を使って表示することがあります。

ただし、オブジェクトをそのまま System.out.println() に渡しても、人が見てすぐ分かる形で表示されるとは限りません。

ここで大切になるのが toString() です。

toString() は、オブジェクトを文字列としてどう表すかを決めるメソッドです。
そして、この toString() は、すべてのクラスの共通の親である Object クラスに最初から用意されています。

鬼滅の刃風にたとえると、toString() は隊士オブジェクトが自分の情報をどう名乗るかを決める自己紹介のようなものです。

たとえば、ある和風剣士オブジェクトを表示したときに、

知りたい情報内容
名前どの隊士なのか
階級どんな立場なのか
担当区域どこを任されているのか

が分かると便利です。

しかし、toString() を自分で作り直していない場合、機械的な表示になることがあります。
そこで、自分のクラスで toString() をオーバーライドすると、オブジェクトを分かりやすい文字列で表示できるようになります。

toString() はオブジェクトを文字列で表すメソッド

toString() は、オブジェクトを文字列で表したものを戻り値として返すメソッドです。

つまり、オブジェクトそのものを、人が見やすい文字列に変換する役割を持っています。

たとえば、次のように書いたとします。

System.out.println(slayer1);

このとき、slayer1 というオブジェクトがそのまま画面に出ているように見えます。
しかし、内部では slayer1 の toString() が呼び出され、その戻り値の文字列が表示されています。

流れを整理すると、次のようになります。

順番起きていること
1System.out.println(slayer1) を実行する
2slayer1 の toString() が呼び出される
3toString() が文字列を返す
4返された文字列が画面に表示される

つまり toString() は、
オブジェクトを表示するときの見え方を決めるメソッド
と考えると分かりやすいです。

鬼滅の刃風にたとえると、隊士が呼ばれたときに「私は水月、階級は水柱です」と名乗るための仕組みです。

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

Objectクラスの toString() が最初から使われている

toString() は、特別なクラスだけが持っているメソッドではありません。

すべてのクラスの共通の親である Object クラスに定義されています。
そのため、自分で toString() を書いていないクラスでも、toString() は使えます。

ただし、Object クラス由来の toString() をそのまま使うと、人にとっては少し分かりにくい表示になることがあります。

たとえば、次のような形式です。

DemonSlayer@1a2b3c

この表示は、Javaとしては正しい表示です。
しかし、隊士の名前や階級が分かるわけではありません。

表示例分かりやすさ
DemonSlayer@1a2b3c機械的で中身が分かりにくい
名前:水月 階級:水柱オブジェクトの内容が分かりやすい

鬼滅の刃風にたとえると、隊士を表示したのに、名前ではなく管理番号だけが出てくるようなものです。

これでは、誰の情報なのかすぐに分かりません。
そこで、自分のクラスで toString() をオーバーライドする価値が出てきます。

なぜ toString() を自分で定義すると便利なのか

toString() を自分のクラスで定義すると、そのオブジェクトをどのように表示するかを自分で決められます。

たとえば、鬼滅の刃風の DemonSlayer クラスなら、次のような情報を文字列にできます。

表示したい情報理由
名前どの隊士なのか分かる
階級立場が分かる
担当区域任務範囲が分かる
呼吸の型クラスの個性が分かる

toString() をオーバーライドしておけば、次のように書くだけで、意味のある情報を表示できます。

System.out.println(slayer1);

毎回、フィールドを1つずつ取り出して文字列を組み立てる必要がありません。

つまり toString() をオーバーライドすると、
そのオブジェクトらしい表示のしかたを、クラス自身に覚えさせられる
ということです。

鬼滅の刃風に考える toString() の感覚

鬼滅の刃風にたとえると、toString() は隊士オブジェクトの自己紹介です。

Object クラス由来のままだと、隊士は少し無機質に、管理用の識別記号だけを見せるような状態になります。

しかし、自分で toString() を作っておけば、次のように名乗れます。

オブジェクトtoString() の表示イメージ
一般隊士名前:蒼真 階級:隊士
水柱名前:水月 階級:水柱
炎柱名前:炎真 階級:炎柱

これは単なる見た目の工夫ではありません。

オブジェクトの内容確認やデバッグのしやすさにもつながります。
特に学習中は、値が正しく入っているかを確認しやすくなるので、とても便利です。

toString() のオーバーライドを確認する

ファイル名:Sample7.java

class DemonSlayer
{
    protected String name;
    protected String rank;

    public DemonSlayer()
    {
        name = "隊士未登録";
        rank = "階級未設定";
        System.out.println("鬼殺隊士を作成しました。");
    }

    public void setSlayer(String n, String r)
    {
        name = n;
        rank = r;
        System.out.println("名前を" + name + "に、階級を" + rank + "にしました。");
    }

    public String toString()
    {
        String str = "名前:" + name + " 階級:" + rank;
        return str;
    }
}

class Sample7
{
    public static void main(String[] args)
    {
        DemonSlayer slayer1 = new DemonSlayer();
        slayer1.setSlayer("水月", "水柱");

        System.out.println(slayer1);
    }
}

実行結果

鬼殺隊士を作成しました。
名前を水月に、階級を水柱にしました。
名前:水月 階級:水柱

Sample7.java の中心になる部分

このプログラムで中心になるのは、次の toString() です。

public String toString()
{
    String str = "名前:" + name + " 階級:" + rank;
    return str;
}

このメソッドでは、name と rank を組み合わせて、隊士を表す文字列を作っています。

そして、その文字列を return で返しています。

つまり DemonSlayer クラスは、自分を表示するときに、

フィールド表示内容
name隊士の名前
rank階級

を見せるように決めているわけです。

このように、toString() はオブジェクトの表示内容を決める役割を持っています。

画面表示のときに何が起きているのか

main メソッドでは、最後に次の1行があります。

System.out.println(slayer1);

見た目には、slayer1 オブジェクトをそのまま出力しているように見えます。

しかし、実際には slayer1 の toString() が呼び出され、その戻り値が表示されています。

流れは次のようになります。

順番処理内容
1System.out.println(slayer1)オブジェクトを表示しようとする
2slayer1.toString()toString() が呼び出される
3文字列を返す名前:水月 階級:水柱 を返す
4画面に表示返された文字列が出力される

つまり、println にオブジェクトを渡したとき、画面に出ているのはオブジェクトそのものではありません。
toString() が返した文字列です。

鬼滅の刃風にたとえると、隊士オブジェクトを呼び出したら、その隊士が自分の名前と階級を名乗ってくれるイメージです。

toString() はオーバーライドの例でもある

toString() は、単なる便利メソッドではありません。

Object クラスにある toString() を、自分のクラスで作り直しているので、これはオーバーライドです。

関係を整理すると、次のようになります。

クラスtoString() の扱い
Objectすべてのクラスが受け継ぐ基本の toString() を持つ
DemonSlayerObject の toString() を自分用に作り直す

つまり、DemonSlayer クラスで次のように書くことは、

public String toString()

Object クラスの toString() をオーバーライドしているということです。

これにより、DemonSlayer オブジェクトを表示したときには、Object 由来の機械的な表示ではなく、DemonSlayer 用に作った表示が使われます。

何もオーバーライドしない場合との違い

toString() をオーバーライドしない場合と、オーバーライドした場合を比べると、違いがはっきりします。

状態表示されるもののイメージ
toString() を自分で定義しないDemonSlayer@1a2b3c のような機械的な表現
toString() をオーバーライドする名前:水月 階級:水柱 のような意味のある表現

この違いは大きいです。

オブジェクトをよく表示するプログラムでは、toString() をオーバーライドしておくと、内容確認がとても楽になります。

たとえば、次のような場面で便利です。

場面便利な理由
値が正しく入ったか確認したいオブジェクトを表示するだけで状態が分かる
配列やリストの中身を見たい各オブジェクトの情報が読みやすい
デバッグしたいどのオブジェクトがどんな状態か追いやすい

どんな文字列を返すとよいのか

toString() の中で返す文字列に、絶対の正解があるわけではありません。

大切なのは、そのオブジェクトを見た人が、何のオブジェクトで、どんな状態なのかを分かりやすくつかめることです。

鬼滅の刃風の DemonSlayer クラスなら、次のような情報が候補になります。

入れやすい情報理由
名前誰のオブジェクトかすぐ分かる
階級役割や立場が分かる
担当区域任務の範囲が分かる
呼吸の型隊士の個性が分かる

ただし、何でも全部入れればよいわけではありません。
長すぎる表示は、かえって見づらくなります。

toString() では、そのクラスを表すうえで大切な情報を、短く分かりやすくまとめるのがポイントです。

toString() を使うと確認や学習がしやすくなる

toString() は、見た目を整えるだけのメソッドではありません。

オブジェクトの中身を確認しやすくするため、学習中にも実務でも役立ちます。

たとえば、次のような確認がしやすくなります。

確認したいことtoString() があると便利な理由
セッターで値が入ったかオブジェクトを表示するだけで確認できる
オブジェクトの状態必要な情報をまとめて見られる
配列やリストの内容各要素の意味が読み取りやすくなる
デバッグ中の状態ログに出したときに分かりやすい

鬼滅の刃風にたとえると、隊士を呼び出すたびに、その隊士が今どんな状態なのかを自分で名乗ってくれるようなものです。

Objectクラスとのつながりを意識することが大切

toString() を理解するときは、Object クラスとのつながりを意識すると整理しやすくなります。

流れは次のとおりです。

流れ内容
1toString() は Object クラスにある
2すべてのクラスは Object を継承している
3だから、どのオブジェクトにも toString() がある
4そのままだと分かりにくい表示になることがある
5自分のクラスでオーバーライドすると分かりやすくできる

この流れで考えると、toString() は特別な裏技ではありません。
Object クラスから受け継いだ基本機能を、自分のクラス向けに作り直す自然なオーバーライドです。

図:toString() が呼ばれる流れ

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

この図が示していること

この図では、System.out.println(slayer1) を実行したときの流れを表しています。

左側にある slayer1 は、name と rank を持つ DemonSlayer オブジェクトです。
このオブジェクトを println に渡すと、中央の toString() が呼び出されます。

toString() は、name と rank を使って文字列を作ります。
そして、その戻り値が右側の出力画面に表示されます。

つまり、画面に出ているのはオブジェクトそのものではなく、toString() が返した文字列です。

図:Object の toString() とオーバーライドの違い

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

この図が示していること

この図では、toString() をオーバーライドしない場合と、オーバーライドした場合の違いを比較しています。

左側は、Object クラス由来の toString() をそのまま使った場合です。
DemonSlayer@1a2b3c のような機械的な表示になり、オブジェクトの中身は分かりにくいです。

右側は、DemonSlayer クラスで toString() をオーバーライドした場合です。
名前:水月 階級:水柱 のように、意味のある情報が表示されています。

この図から分かることは、toString() をオーバーライドすると、オブジェクトが自分の状態を分かりやすく名乗れるようになるということです。

鬼滅の刃風に toString() を整理する

toString() は、隊士オブジェクトに「自分をどう名乗るか」を決めさせるメソッドです。

何も決めていなければ、Object クラスから受け継いだ機械的な名乗り方になります。

しかし、自分のクラスで toString() をオーバーライドすれば、

表示できる情報
名前水月
階級水柱
担当区域第7区域
呼吸の型水の呼吸

のような情報を、分かりやすく返せます。

つまり toString() は、
オブジェクトを人に伝わる言葉へ変えるための仕組み
です。

そして、それを自分のクラスらしく作り直すことが、toString() のオーバーライドです。

この内容で押さえておきたいポイント

ポイント内容
toString()オブジェクトを文字列で表すメソッド
定義元Object クラスに定義されている
すべてのクラスにある理由すべてのクラスは Object を継承しているから
println との関係オブジェクトを渡すと toString() の戻り値が表示される
オーバーライドしない場合DemonSlayer@1a2b3c のような機械的な表示になりやすい
オーバーライドする場合名前や階級など、意味のある表示にできる
便利な場面デバッグ、学習中の確認、配列やリストの表示

toString() をオーバーライドすると、オブジェクトの状態がぐっと見やすくなります。

オブジェクトをただ作るだけでなく、表示したときに何を伝えるかまでクラスに持たせる。
これが toString() を使う大きな意味です。