Java入門|toString()の役割とオーバーライド(オブジェクト表示のしくみ)

オブジェクトは、そのままでは少し無口です。
toString をオーバーライドすると、戦士の情報をわかりやすく名乗らせることができるようになります。

Javaでは、オブジェクトを画面に表示したい場面がよくあります。
たとえば、今作ったオブジェクトにどんな値が入っているのかを確かめたいときや、処理の途中で状態を見たいときです。

ところが、オブジェクトをそのまま System.out.println に渡しただけでは、人が見てすぐ意味のわかる形で表示されるとは限りません。
ここで大事になるのが toString() です。toString() は、オブジェクトを文字列でどう表すかを決めるためのメソッドです。しかもこれは、すべてのクラスの共通の親である Object クラスに最初から用意されています。

ドラゴンボールでたとえると、これは戦士が自分の情報をどう名乗るか、という話に近いです。

たとえば戦士オブジェクトをそのまま表示したときに、

  • 誰なのか
  • 戦闘力はいくつなのか
  • どんな状態なのか

がわからない表示しか出ないと、少し困りますよね。
でも toString() を自分でオーバーライドしておけば、その戦士が

  • 名前
  • 戦闘力
  • 追加情報

などを、わかりやすい文字列として返せるようになります。

今回は、そんな toString() の役割とオーバーライドの意味を、ドラゴンボールの世界観で置きかえながら、やさしく丁寧に整理していきます。特に、

  • toString() は何をするメソッドなのか
  • オブジェクトを出力するときに何が起きているのか
  • 自分のクラスで toString() を定義すると何が便利になるのか
  • オーバーライドとしてどう考えればよいのか

を順番に見ていきます。

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

まず、toString() は何をするメソッドなのかをはっきりさせましょう。

toString() は、オブジェクトを文字列で表したものを戻り値として返すメソッド です。
つまり、オブジェクトそのものを人に見せやすい形へ変換する役目を持っています。

Javaでは、オブジェクトを画面に表示しようとしたとき、この toString() が使われます。
たとえば次のように書いたとします。

System.out.println(warrior1);

このとき、ただオブジェクトがそのまま出力されるのではなく、内部では toString() が呼び出され、その戻り値である文字列が表示されます。

つまり toString() は、
オブジェクトの見た目を決める自己紹介メソッド
と考えるとわかりやすいです。

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

toString() は特別なクラスだけの機能ではありません。
すべてのクラスの共通の親である Object クラスに最初から定義されています。だから、自分で何も書かなくても、どのクラスのオブジェクトにも toString() は存在しています。

ただし、何も準備しないまま Objectクラス由来の toString() を使うと、人にとっては少しわかりにくい文字列になります。Saiyan@数値 のような形式の文字列が表示されます。(※数値は16進数)これは機械的な識別には使えても、オブジェクトの中身を知るにはあまり向いていません。

ドラゴンボールでたとえるなら、戦士を表示したのに、

  • 名前が出ない
  • 戦闘力も出ない
  • ただ識別番号のようなものだけが出る

ような状態です。

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

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

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

たとえばドラゴンボール風の Saiyan クラスなら、

  • 戦士名
  • 戦闘力
  • 所属エリア
  • 必殺技

などの中から、表示したいものを選んで、わかりやすい形の文字列にできます。

そうすると、System.out.println(warrior1); と書いただけで、意味のある内容が見られるようになります。

これはとても便利です。
なぜなら、毎回

  • フィールドを1つずつ取り出して
  • 文字列を組み立てて
  • 表示文を書いて

という手間を減らせるからです。

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

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

ドラゴンボールで考えると、toString() は戦士の自己紹介のようなものです。

Objectクラス由来のままだと、その戦士は少し無機質に

  • 識別記号
  • 管理用の番号

のようなものしか見せてくれません。

でも、自分で toString() を作っておけば、その戦士はたとえば

  • 悟空 戦闘力 9000
  • ベジータ 戦闘力 18000
  • 悟飯 戦闘力 12000

のように、自分の情報をわかりやすく名乗れるようになります。

これは単なる見た目の工夫ではありません。
オブジェクトの内容確認やデバッグのしやすさにもつながる、とても大事な工夫です。

サンプルプログラム: toString() をオーバーライド

ここでは、ドラゴンボール風のクラスで toString() をオーバーライドする例を見ていきます。
ここでは、name と power を持つ戦士クラスを用意し、その情報を文字列として返す形にします。

ファイル名:Sample7.java

class Saiyan
{
    protected String name;
    protected int power;

    public Saiyan()
    {
        name = "戦士なし";
        power = 0;
        System.out.println("サイヤ人を作成しました。");
    }

    public void setSaiyan(String n, int p)
    {
        name = n;
        power = p;
        System.out.println("名前を" + name + "に戦闘力を" + power + "にしました。");
    }

    public String toString()
    {
        String str = "名前:" + name + " 戦闘力:" + power;
        return str;
    }
}

class Sample7
{
    public static void main(String[] args)
    {
        Saiyan warrior1 = new Saiyan();
        warrior1.setSaiyan("ベジータ", 18000);

        System.out.println(warrior1);
    }
}

実行結果

サイヤ人を作成しました。
名前をベジータに戦闘力を18000にしました。
名前:ベジータ 戦闘力:18000

このプログラムで大事なところ

このプログラムで中心になるのは、次の部分です。

public String toString()
{
    String str = "名前:" + name + " 戦闘力:" + power;
    return str;
}

ここでは name と power を組み合わせて、戦士を表す文字列を作っています。
そしてその文字列を戻り値として返しています。

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

  • 名前
  • 戦闘力

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

これが toString() の役割です。

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

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

System.out.println(warrior1);

見た目には、オブジェクト warrior1 をそのまま出力しているように見えます。
でも、実際には toString() の戻り値が使われています。

つまり流れとしては、

  1. warrior1 を表示しようとする
  2. warrior1 の toString() が呼ばれる
  3. 戻り値の文字列が得られる
  4. その文字列が画面に表示される

ということです。

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

順番起きていること
1System.out.println(warrior1) を実行する
2warrior1 の toString() が呼び出される
3名前:ベジータ 戦闘力:18000 のような文字列が返る
4その文字列が出力される

つまり println にオブジェクトを渡したとき、見えているのはオブジェクトそのものではなく、toString() が返した自己紹介文だと考えるとわかりやすいです。

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

ここで、toString() は単なる便利メソッドではなく、Objectクラスのメソッドをオーバーライドしている という点も大切です。

もともと toString() は Objectクラスにあります。
それを Saiyan クラス側で自分用に定義しなおしているので、これはまさにオーバーライドです。

つまり、

  • 親クラス Object に toString() がある
  • 子クラス Saiyan で同じ toString() を定義する
  • すると Saiyan オブジェクトでは Saiyan 版の toString() が使われる

という流れです。

ここでも、これまで学んできたオーバーライドの考え方がそのまま使われています。

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

オーバーライドしない場合と、した場合の違いを表にするとかなりわかりやすいです。

状態表示されるもののイメージ
toString() を自分で定義しないSaiyan@数値 のような機械的な表現
toString() をオーバーライドする名前:ベジータ 戦闘力:18000 のような意味のある表現

この違いはかなり大きいです。

とくにオブジェクトをよく表示するプログラムでは、toString() をオーバーライドしておくと、内容確認がとても楽になります。本文でも、オブジェクトをひんぱんに出力する場合には便利だと説明されています。

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

toString() の中で返す文字列には決まった正解があるわけではありません。
大切なのは、そのオブジェクトを見た人が「何のオブジェクトで、どんな状態か」をわかりやすくつかめることです。

ドラゴンボール風に考えるなら、たとえば次のような情報が候補になります。

入れやすい情報理由
名前誰のオブジェクトかすぐわかる
戦闘力状態の違いが見えやすい
所属や担当役割が把握しやすい
必殺技クラスの個性を表しやすい

ただし、何でも全部入れればよいとは限りません。
長すぎると逆に見づらくなることもあります。
なので、そのクラスを表すのに大事な情報を、見やすい形でまとめるのがポイントです。

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

toString() は、見た目をきれいにするだけではありません。
オブジェクトの中身を確認しやすくするので、学習中にもとても役立ちます。

たとえば、

  • セッターメソッドで値が正しく入ったか確認したい
  • オブジェクトの状態を途中で表示したい
  • 配列やコレクションに入ったオブジェクトを見やすくしたい

といった場面で便利です。

ドラゴンボールで言えば、戦士を呼び出すたびに、その戦士が今どんな状態なのかをすぐ言葉で確かめられるようなものです。

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

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

  • toString() は最初から Objectクラスにある
  • だからどんなクラスのオブジェクトにも toString() がある
  • でもそのままだとわかりにくいことがある
  • そこで自分のクラスでオーバーライドして、意味のある文字列にする

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

図で toString() の流れを整理する

toString() は、呼び出しの流れを図で見るとかなりわかりやすくなります。

左側の warrior1 は、名前と戦闘力を持つ戦士オブジェクトです。
このオブジェクトをそのまま System.out.println に渡すと、中央の toString() が呼ばれます。

そして toString() が返した文字列が、右側の画面表示として出てきます。
この流れから、オブジェクトを出力しているように見えて、実際には toString() が返した文字列を表示していることがわかります。

Objectクラスから受け継いだ toString() を自分のクラスでオーバーライドすることで、表示内容を自由に決めることができます。

ドラゴンボールで感覚的に整理する

最後に、ドラゴンボールの感覚でまとめてみます。

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

何も決めていなければ、Objectクラスから受け継いだ、少し機械的な名乗り方になります。
でも、自分のクラスで toString() をオーバーライドすれば、

  • 名前
  • 戦闘力
  • 必要な追加情報

を、わかりやすい言葉で返せるようになります。

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

そしてそれを自分のクラスらしく作り直すことが、toString() のオーバーライドです。
この感覚がつかめると、Objectクラスの基本メソッドが、実際のクラス設計の中でどう生きるのかがとても見えやすくなります。