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

オブジェクトは、そのまま表示すると少し無口です。toString() をオーバーライドすると、戦士オブジェクト自身に「名前・流派・役割」を分かりやすく名乗らせることができます。

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

たとえば、サイヤ人戦士オブジェクトを作ったあとに、名前や流派が正しく入っているか確認したいときです。
また、プログラムの途中で、今そのオブジェクトがどんな状態なのかを見たいときにも、System.out.println() を使って表示することがあります。

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

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

toString() は、オブジェクトを文字列としてどう表すかを決めるメソッドです。

そして、この toString() は、すべてのクラスの共通の親である Object クラスに最初から用意されています。

ドラゴンボール風にたとえると、toString() は戦士オブジェクトが自分の情報をどう名乗るかを決める自己紹介のようなものです。

たとえば、あるサイヤ人戦士オブジェクトを表示したときに、次の情報が分かると便利です。

知りたい情報内容
名前どの戦士なのか
流派どの流派に属しているのか
戦闘スタイルどんな役割の戦士なのか
変身段階どの状態まで覚醒しているのか

しかし、toString() を自分で作り直していない場合、機械的な表示になることがあります。

そこで、自分のクラスで toString() をオーバーライドすると、オブジェクトを分かりやすい文字列で表示できるようになります。

この記事では、具体的なプログラムとして Sample7.java を例示しながら、toString() の役割、Object クラスとの関係、System.out.println() との関係、toString() のオーバーライドの意味を、ドラゴンボールの世界観で解説します。

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

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

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

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

System.out.println(warrior1);

このとき、warrior1 というオブジェクトがそのまま画面に出ているように見えます。

しかし、内部では warrior1 の toString() が呼び出され、その戻り値の文字列が表示されています。

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

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

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

ドラゴンボール風にたとえると、戦士が呼ばれたときに「私は悟空、流派は亀仙流です」と名乗るための仕組みです。

オブジェクトは内部に情報を持っています。
しかし、その情報をどのような文字列として外に見せるかは、toString() で決められます。

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

この図が示していること

この図では、System.out.println(warrior1) を実行したときに、内部で toString() が呼び出される流れを表しています。

左側にある warrior1 は、name と styleName を持つ SaiyanWarrior オブジェクトです。

このオブジェクトを println に渡すと、中央の toString() が呼び出されます。

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

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

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

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

すべてのクラスの共通の親である Object クラスに定義されています。

そのため、自分で toString() を書いていないクラスでも、toString() は使えます。

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

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

SaiyanWarrior@1a2b3c

この表示は、Javaとしては正しい表示です。

しかし、戦士の名前や流派が分かるわけではありません。

表示例分かりやすさ
SaiyanWarrior@1a2b3c機械的で中身が分かりにくい
名前:悟空 流派:亀仙流オブジェクトの内容が分かりやすい

ドラゴンボール風にたとえると、戦士を表示したのに、名前ではなく管理番号だけが出てくるようなものです。

これでは、誰の情報なのかすぐに分かりません。

そこで、自分のクラスで toString() をオーバーライドする価値が出てきます。

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

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

たとえば、ドラゴンボール風の SaiyanWarrior クラスなら、次のような情報を文字列にできます。

表示したい情報理由
名前どの戦士なのか分かる
流派亀仙流、鶴仙流、神流などの所属が分かる
戦闘スタイル戦士の役割や個性が分かる
変身段階覚醒状態が分かる

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

System.out.println(warrior1);

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

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

ドラゴンボール風に言えば、戦士オブジェクトに「呼ばれたら、自分の名前と流派をこう名乗りなさい」と教えておくようなものです。

ドラゴンボール風に考える toString() の感覚

ドラゴンボール風にたとえると、toString() は戦士オブジェクトの自己紹介です。

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

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

オブジェクトtoString() の表示イメージ
一般戦士名前:天津飯 流派:鶴仙流
亀仙流の戦士名前:悟空 流派:亀仙流
神流の覚醒戦士名前:悟飯 流派:神流

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

オブジェクトの内容確認やデバッグのしやすさにもつながります。

特に学習中は、値が正しく入っているかを確認しやすくなるので、とても便利です。

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

ファイル名:Sample7.java

class SaiyanWarrior
{
    protected String name;
    protected String styleName;

    public SaiyanWarrior()
    {
        name = "戦士未登録";
        styleName = "流派未設定";
        System.out.println("サイヤ人戦士を作成しました。");
    }

    public void setWarrior(String n, String s)
    {
        name = n;
        styleName = s;
        System.out.println("名前を" + name + "に、流派を" + styleName + "にしました。");
    }

    public String toString()
    {
        String str = "名前:" + name + " 流派:" + styleName;
        return str;
    }
}

class Sample7
{
    public static void main(String[] args)
    {
        SaiyanWarrior warrior1 = new SaiyanWarrior();
        warrior1.setWarrior("悟空", "亀仙流");

        System.out.println(warrior1);
    }
}

実行結果

サイヤ人戦士を作成しました。
名前を悟空に、流派を亀仙流にしました。
名前:悟空 流派:亀仙流

Sample7.java の中心になる部分

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

public String toString()
{
    String str = "名前:" + name + " 流派:" + styleName;
    return str;
}

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

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

つまり SaiyanWarrior クラスは、自分を表示するときに、次の情報を見せるように決めているわけです。

フィールド表示内容
name戦士の名前
styleName流派

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

ここでは、戦士の名前と流派を短くまとめて返しています。

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

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

System.out.println(warrior1);

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

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

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

順番処理内容
1System.out.println(warrior1)オブジェクトを表示しようとする
2warrior1.toString()toString() が呼び出される
3文字列を返す名前:悟空 流派:亀仙流 を返す
4画面に表示返された文字列が出力される

つまり、println にオブジェクトを渡したとき、画面に出ているのはオブジェクトそのものではありません。

toString() が返した文字列です。

ドラゴンボール風にたとえると、戦士オブジェクトを呼び出したら、その戦士が自分の名前と流派を名乗ってくれるイメージです。

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

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

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

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

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

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

public String toString()

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

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

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

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

状態表示されるもののイメージ
toString() を自分で定義しないSaiyanWarrior@1a2b3c のような機械的な表現
toString() をオーバーライドする名前:悟空 流派:亀仙流 のような意味のある表現

この違いは大きいです。

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

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

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

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

この図が示していること

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

左側は、Object クラス由来の toString() をそのまま使った場合です。

SaiyanWarrior@1a2b3c のような機械的な表示になり、オブジェクトの中身は分かりにくいです。

右側は、SaiyanWarrior クラスで toString() をオーバーライドした場合です。

名前:悟空 流派:亀仙流 のように、意味のある情報が表示されています。

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

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

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

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

ドラゴンボール風の SaiyanWarrior クラスなら、次のような情報が候補になります。

入れやすい情報理由
名前誰のオブジェクトかすぐ分かる
流派亀仙流、鶴仙流、神流などの所属が分かる
戦闘力強さの目安が分かる
変身段階覚醒状態が分かる
役割攻撃型、支援型、回復型などが分かる

ただし、何でも全部入れればよいわけではありません。

長すぎる表示は、かえって見づらくなります。

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

たとえば、学習用なら次のような表示が分かりやすいです。

名前:悟空 流派:亀仙流

もう少し情報を増やしたい場合は、次のようにしてもよいです。

名前:悟空 流派:亀仙流 戦闘力:9000

大切なのは、表示する目的に合わせて情報を選ぶことです。

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

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

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

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

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

ドラゴンボール風にたとえると、戦士を呼び出すたびに、その戦士が今どんな状態なのかを自分で名乗ってくれるようなものです。

特に、複数のオブジェクトを扱うようになると、toString() のありがたさがよく分かります。

たとえば、戦士オブジェクトを配列やリストに入れて順番に表示するとき、toString() が整っていると、各戦士の状態をすぐ確認できます。

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

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

流れは次のとおりです。

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

この流れで考えると、toString() は特別な裏技ではありません。

Object クラスから受け継いだ基本機能を、自分のクラス向けに作り直す自然なオーバーライドです。

図:Object クラスとのつながりと toString() の役割

この図が示していること

この図では、toString() が Object クラスから受け継がれ、それを SaiyanWarrior クラスでオーバーライドする流れを表しています。

上部の Object クラスには、toString() が用意されています。

SaiyanWarrior クラスは Object クラスにつながっているため、toString() を受け継いでいます。

ただし、そのまま使うと機械的な表示になりやすいため、SaiyanWarrior クラスで public String toString() を定義し、自分のクラス用に作り直しています。

その結果、System.out.println(warrior1) を実行したときに、名前:悟空 流派:亀仙流 のような分かりやすい文字列が表示されます。

この図から分かるのは、toString() は Object から受け継いだ基本機能であり、必要に応じて自分のクラスらしくオーバーライドできるということです。

ドラゴンボール風に toString() を整理する

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

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

しかし、自分のクラスで toString() をオーバーライドすれば、次のような情報を分かりやすく返せます。

表示できる情報
名前悟空
流派亀仙流
戦闘力9000
役割攻撃型戦士
変身段階1

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

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

ドラゴンボール風に言えば、ただ無言で管理番号を見せる戦士ではなく、名前や流派をしっかり名乗れる戦士にするようなものです。

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

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

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

オブジェクトをただ作るだけでなく、表示したときに何を伝えるかまでクラスに持たせる。

これが toString() を使う大きな意味です。