読み込み中...

Javaで使うtoStringメソッドのサンプルコード完全ガイド12選

JavaのtoStringメソッドを使ったサンプルコードのイメージ Java
この記事は約37分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

JavaのtoStringは、オブジェクトの中身をログ、画面表示、デバッグ出力で読み取れる文字列へ変える入口になります。そのため、既定の表示に頼る場面と、クラスごとに上書きして読みやすくする場面を分けると、toStringメソッドの使い方を整理できます。

そのため、最初に押さえるべき結論は明確です。toStringメソッドは、オブジェクトの全情報を詰め込む場所ではなく、確認に必要な状態を短く返すためのメソッドとして設計するのが基本です。

ただし、短くしすぎると原因調査に必要な値が不足します。識別子、名称、状態、件数のように、ログを読む人が次の調査へ進める情報を選ぶと、表示と保守のバランスを取りやすくなります。

動作確認環境
  • Java 21 LTS / OpenJDK 21
  • Apache Commons Lang 3.14.0 / Gson 2.10.1

このとき、公式仕様を確認するならOracle Java APIのObject#toStringが一次情報になるのが目安です。コレクションの表示規則はAbstractCollection#toStringにも記載があり、リストやセットの出力を読む手がかりになります。

📖 この記事で学べること
  • toStringメソッドが呼び出される仕組みと既定表示の読み方
  • サンプルコードを通じた基本的なオーバーライドの書き方
  • リスト、日付、条件分岐、外部ライブラリを使う応用例
  • null、巨大データ、機密情報に関する注意点と対処法
  • JSON風出力や複数オブジェクト連結のカスタマイズ

JavaのtoStringメソッドとは

JavaのtoStringメソッドは、Objectクラスに定義された文字列表現用のメソッドです。すべてのクラスは直接または間接的にObjectを継承するため、自作クラスでもtoString()を呼び出せます。

ただし、既定のObject#toStringはクラス名とハッシュコード由来の値を返すため、業務データや学習用データの内容を読むには情報量が足りない場合があります。そのため、名前、年齢、状態、識別子などのフィールドを含めたいクラスでは、@Overrideを付けて独自の文字列表現を返す設計が一般的に選ばれますし、ここがポイントです。

観点既定の挙動カスタマイズ後使いどころ注意点
通常オブジェクトClassName@hash任意のフィールド表示状態確認機密値を出さない
リスト[a, b]形式区切り文字や順序を調整ログ出力大量要素は省略する
日付処理系の既定形式yyyy/MM/ddなど画面や帳票タイムゾーンに注意する
null呼び出しで例外事前判定で代替文字列入力値確認NullPointerExceptionを避ける
JSON風文字列対象外{"name":"Taro"}簡易確認本格変換はライブラリを使う
外部ライブラリ標準のみToStringBuilderなど多フィールド出力依存管理が必要

これらの観点を分けると、toStringメソッドの設計は単なる文字列連結ではなく、クラスの状態をどの粒度で外へ見せるかという判断になります。特に押さえたいのは、読みやすさ、情報量、秘匿性、処理負荷のバランスです。

具体的には、識別に必要な値だけを返す短い形式、調査時に役立つ詳細形式、外部ライブラリで整えた多行形式を使い分けます。ひとつのクラスで複数の表現が必要になるなら、toStringメソッドへ詰め込まず、目的別のメソッド名を用意するほうが読み手に親切です。

toStringメソッドの基本

一般に、System.out.println(obj)へオブジェクトを渡すと、内部的には文字列化が行われますが、これは押さえたい点です。この変換の中心にあるのがtoStringメソッドで、明示的にobj.toString()と書く場合も、出力APIが暗黙に呼ぶ場合もあります。

そのため、クラスの内容を自然な形で表示したいなら、public String toString()というシグネチャを守って上書きします。戻り値はStringで、例外を投げずに短時間で返せる処理にしておくと扱いやすくなるのがポイントです。

💡 Tips: @Overrideを付けると、メソッド名や戻り値を間違えたときにコンパイル時点で気づけます。toStringメソッドのカスタマイズでは、この注釈を付ける書き方が読み手にも意図を伝えます。

この基本を踏まえると、toStringメソッドの戻り値は、クラスを知らない人でも意味を推測できる形式にするのが実用的です。単に値だけを並べるより、name=age=のようなラベルを添えると、ログの一部だけを見たときにも判断しやすくなるのが一般的です。

ただし、ラベルを日本語にするか英語にするかは、プロジェクトのログ方針に合わせます。アプリケーションの利用者向け表示なら日本語が読みやすい一方、開発者向けログではフィールド名と同じ英語表記のほうが検索しやすい場合があります。

これを理解すると、printlnにオブジェクトを渡しただけで読みやすい文字列が出る理由も説明できるのが現実的です。出力処理側が値を文字列へ変換する過程で、対象オブジェクトのtoStringメソッドが参照されるためです。

逆に、既定表示のままでは、クラス名以外の意味を読み取れないことがあります。初心者がつまずきやすいのは、Student@1a2b3cのような出力を見て、フィールドが正しく設定されていないと誤解する点です。

Javaオブジェクトと文字列変換の関係

オブジェクトは複数のフィールドを持つため、そのままでは人間が状態を把握しにくい構造です。文字列へ変換すると、ログ、例外メッセージ、テスト失敗時の差分、簡易的な画面表示で同じ情報を再利用できます。

一方、toStringメソッドは本来、プログラム間の厳密なデータ交換形式を保証するものではありません。CSVやJSONとして外部システムへ渡す用途では、GsonJacksonなどのシリアライズ用ライブラリを選ぶほうが適していると整理できます。

基本的に、toStringメソッドは「保存用の完全データ」ではなく「読み取り用の要約」を返す場所です。たとえば、顧客オブジェクトならIDと表示名、注文オブジェクトなら注文番号と状態のように、確認時に必要な値を絞ると読みやすくなります。

一方、すべてのフィールドを無条件に並べると、ログが長くなり、変化の多い値に埋もれて本当に確認したい情報が見えにくくなります。toStringメソッドのカスタマイズでは、出力する値を増やす判断と同じくらい、出さない値を決める判断が必要になると理解できます。

関連する基礎として、配列やリストの扱いはJava List型完全ガイドと合わせて読むと理解しやすくなります。メソッドの上書きに不安がある場合は、Javaのオーバーライド解説も参照できます。

toStringメソッドの使い方

toStringメソッドの使い方は、既定表示を確認し、自作クラスで上書きし、継承関係でも親クラスの表示を活かす流れで覚えると整理できると覚えるとよいでしょう。サンプルコードごとに、期待される出力と読みどころを分けて確認します。

これらのサンプルコードでは、publicprivateclassnewreturnなどの基本構文も同時に使います。toStringメソッドだけを切り離して覚えるより、クラス定義、コンストラクタ、フィールド、出力処理の流れと一緒に読むほうが理解しやすくなると考えられます。

そのため、サンプルコードを手元で動かす場合は、ファイル名とpublic class名を合わせる必要があります。複数のPersonクラスが登場するため、同じフォルダに置くならファイルを分ける、またはクラス名を変える調整が必要です。

使い方を分類すると、既定表示の確認、フィールドの列挙、親クラス表示の再利用、外部ライブラリへの委譲に分けられます。どのパターンでも、戻り値がnullにならないようにし、処理が重くなりすぎない範囲に収めますし、これが一つの目安です。

具体的には、returnする文字列を作るだけの処理にとどめ、データベースアクセスやネットワーク通信を含めないほうが安全です。toStringメソッドは予想外のタイミングで呼ばれる可能性があるため、副作用のない実装に寄せます。

サンプルコード1:基本的なオブジェクトの文字列変換

このサンプルコードは、Objectのインスタンスを作り、既定のtoString()を呼び出します。独自クラスで上書きしない場合の表示を知っておくと、ログに出る@付き文字列の意味を読み違えにくくなると言えるでしょう。

// Objectクラスのインスタンスを作成
Object obj = new Object();

// toStringメソッドを呼び出し、結果を出力
System.out.println(obj.toString());

結果: 期待される出力はjava.lang.Object@74a14482のような形式です。末尾の値は環境や実行タイミングで変わるため、固定値として扱わないほうが安全です。

この設計を続けると、ログを検索するときの軸もそろいます。たとえばid=status=count=のような表記を統一しておくと、障害調査やテスト失敗時の確認で対象オブジェクトを絞り込みやすくなります。

一方、文章として自然な表示だけを優先すると、機械的な検索が難しくなる場合があるのが基本です。読みやすい日本語ラベルと検索しやすいフィールド名のどちらを採用するかは、ログを読む人と利用目的に合わせて決めます。

サンプルコード2:カスタマイズした文字列変換

その既定表示ではフィールドの中身が分からないため、Studentクラスでnameageを返す形にカスタマイズします。System.out.println(student)のように書いても同じ文字列表現が使われますが、覚えておくと役立つでしょう。

public class Student {
  private String name;
  private int age;

  public Student(String name, int age) {
    this.name = name;
    this.age = age;
  }

  // toStringメソッドをオーバーライド
  @Override
  public String toString() {
    return "Student{" +
           "name='" + name + ''' +
           ", age=" + age +
           '}';
  }

  public static void main(String[] args) {
    Student student = new Student("Taro", 20);
    System.out.println(student.toString());
  }
}

結果: 期待される出力はStudent{name='Taro', age=20}です。クラス名とフィールド名を含めると、複数のオブジェクトが並んだログでも内容を判別しやすくなります。

この形式では、Studentというクラス名が先頭にあり、波括弧の中へフィールドを並べています。オブジェクトが配列やリストに入ったときも同じ形式で表示されるため、あとから集合データへ拡張しても読み方が変わりません。

一方、ユーザー画面へそのまま出す文言としては、クラス名やフィールド名が硬く見える場合があるのが目安です。画面表示用の文言はView層で整え、toStringメソッドは開発者が状態を追うための表現に寄せると役割が混ざりにくくなります。

サンプルコード3:独自のクラスでのtoStringメソッドのオーバーライド

継承したクラスでは、親クラスの表示をsuper.toString()で再利用できます。この使い方により、共通フィールドの出力を親へ集約し、子クラスでは追加フィールドだけを足せますし、ここを基本と考えるとよいでしょう。

public class CollegeStudent extends Student {
  private String major;

  public CollegeStudent(String name, int age, String major) {
    super(name, age);
    this.major = major;
  }

  // toStringメソッドをオーバーライド
  @Override
  public String toString() {
    return super.toString() + ", major='" + major + ''' ;
  }

  public static void main(String[] args) {
    CollegeStudent collegeStudent = new CollegeStudent("Taro", 20, "Engineering");
    System.out.println(collegeStudent.toString());
  }
}

結果: 期待される出力はStudent{name='Taro', age=20}, major='Engineering'です。親の表示形式を変更すると子の表示にも反映されるため、継承階層では出力形式の責務を決めておく必要があります。

継承関係で出力を組み立てる場合、親クラスのtoStringメソッドが返す形式に子クラスが依存します。そのため、親の戻り値を大きく変更すると、子の表示が不自然になる可能性があるのがポイントです。

このとき、親クラス側でprotectedな補助メソッドを用意し、子クラスが必要なフィールドだけを組み立てる設計も選べます。小さな学習用コードではsuper.toString()で十分ですが、継承階層が深い場合は変更耐性を意識します。

toStringメソッドの応用例

基本の使い方が分かると、コレクション、日付、条件分岐、外部ライブラリへ応用できるのが一般的です。応用例では、表示したい相手が人間なのか、ログ解析ツールなのか、外部システムなのかを分けて考えると設計しやすくなります。

このとき、戻り値の形式はクラスの公開インターフェースほど厳密に固定されない場合があります。ログやデバッグ向けの表現として使うなら、読みやすさを優先しつつ、テストで文字列の完全一致に依存しすぎない設計が扱いやすくなるのが現実的です。

ただし、監査ログや外部連携のキーとして文字列を使う場合は、toStringメソッドではなく専用の変換メソッドを用意するほうが明確です。たとえばtoCsvLine()toLogMessage()のように目的を名前へ含めると、後から読む人が意図を判断しやすくなります。

サンプルコード4:リストの要素を一つの文字列にまとめる

Javaのリストは、要素全体を角括弧で囲んだ文字列へ変換できます。Listそのものの内容を簡単に確認したい場合、このサンプルコードのようにnumbers.toString()を使えると整理できます。

import java.util.Arrays;
import java.util.List;

public class Main {
  public static void main(String[] args) {
    // リストを作成
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

    // リストの要素を一つの文字列にまとめる
    String result = numbers.toString();

    // 結果を出力
    System.out.println(result);
  }
}

結果: 期待される出力は[1, 2, 3, 4, 5]です。Listの各要素に対しても文字列化が行われるため、要素が自作クラスなら、そのクラスのtoStringメソッドが読みやすさを左右します。

リストの文字列表現では、要素の順序がそのまま出力に反映されます。Arrays.asListで作った例は順序が安定していますが、HashSetなど順序保証がないコレクションでは、表示順が期待と異なる場合があると理解できます。

そのため、順序まで意味を持つログを残すなら、Listを使う、または出力前にソートするなどの対処が必要になります。コレクションのtoStringメソッドは状態確認には向きますが、順序の保証は型ごとの仕様に従います。

サンプルコード5:日付オブジェクトを独自のフォーマットの文字列に変換

日付は既定の文字列表現だけでは、画面や帳票で求められる形式と合わない場合があると覚えるとよいでしょう。そのため、SimpleDateFormatで整形した値を返すと、表示形式をクラス内に閉じ込められます。

import java.text.SimpleDateFormat;
import java.util.Date;

public class CustomDate {
  private Date date;

  public CustomDate(Date date) {
    this.date = date;
  }

  // toStringメソッドをオーバーライド
  @Override
  public String toString() {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    return sdf.format(date);
  }

  public static void main(String[] args) {
    CustomDate customDate = new CustomDate(new Date());
    System.out.println(customDate.toString());
  }
}

結果: 期待される出力は2026/06/21のような日付文字列です。日付は実行日やタイムゾーンで変わるため、固定のサンプル値ではなく形式を確認する例として扱います。

日付の扱いでは、java.util.DateSimpleDateFormatの例を示していますが、新しいコードではjava.time.LocalDateDateTimeFormatterを選ぶ場面も多くあります。既存コードとの互換性がある場合を除き、日付時刻APIの選定も合わせて確認すると考えられます。

一方、toStringメソッドの中で現在時刻を毎回生成すると、同じオブジェクトでも呼び出しごとに表示が変わる設計になります。状態確認のためのメソッドでは、オブジェクトが保持している値を返すほうが予測しやすくなります。

サンプルコード6:複数のオブジェクト属性を一つの文字列に結合

複数のフィールドを持つクラスでは、利用者が知りたい値を一文で読めるように並べますし、ここがポイントです。nameageを連結するだけでも、既定表示より状態が明確になります。

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // toStringメソッドをオーバーライド
    @Override
    public String toString() {
        // nameとageを結合して返す
        return "名前: " + name + ", 年齢: " + age;
    }

    public static void main(String[] args) {
        Person person = new Person("Taro", 25);
        System.out.println(person.toString());
    }
}

結果: 期待される出力は名前: Taro, 年齢: 25です。intなどのプリミティブ値は文字列連結時に変換されるため、この例では明示的なString.valueOfを使わなくても構いません。

日付、数値、真偽値をまとめて表示する場合は、型ごとの意味が失われないようにラベルを添えます。truefalseのままでも開発者には伝わりますが、利用者向けの説明に近いログでは「有効」「無効」のような語に変換する判断もあります。

ただし、toStringメソッド内で地域設定や言語設定まで細かく扱うと、表示責務が膨らみますが、これは押さえたい点です。多言語対応が必要な画面文言は、リソースファイルやフォーマッタへ任せ、toStringメソッドは開発者向けの安定した表現に保つと整理できます。

サンプルコード7:条件に応じた動的な文字列変換

状態によって表示を変えたい場合は、ifelseをtoStringメソッド内に置けます。ただし、条件分岐が増えすぎると読みにくくなるため、表示の種類が多いクラスでは別メソッドへ切り出す設計も検討すると言えるでしょう。

public class Person {
    private String name;
    private int age;
    private boolean isStudent;

    public Person(String name, int age, boolean isStudent) {
        this.name = name;
        this.age = age;
        this.isStudent = isStudent;
    }

    // toStringメソッドをオーバーライド
    @Override
    public String toString() {
        if (isStudent) {
            return "名前: " + name + ", 年齢: " + age + ", 学生: はい";
        } else {
            return "名前: " + name + ", 年齢: " + age + ", 学生: いいえ";
        }
    }

    public static void main(String[] args) {
        Person student = new Person("Taro", 25, true);
        Person nonStudent = new Person("Hanako", 30, false);
        System.out.println(student.toString());
        System.out.println(nonStudent.toString());
    }
}

結果: 期待される出力は、true側が名前: Taro, 年齢: 25, 学生: はいfalse側が名前: Hanako, 年齢: 30, 学生: いいえです。boolean値をそのまま出すより、日本語ラベルへ変えると利用者向けの表示になります。

条件分岐を入れる場合、表示の分岐条件がビジネスルールと混ざらないように注意します。学生かどうかを文字列へ変える程度なら問題ありませんが、料金計算や権限判定のような処理をtoStringメソッドへ入れると責務が広がりすぎますし、これが一つの目安です。

そのため、状態の判定は別メソッドで行い、toStringメソッドでは判定済みの状態をラベル化するだけに抑える設計が扱いやすくなります。表示に必要な条件が増えた場合も、テスト対象を分けやすくなります。

サンプルコード8:外部ライブラリを利用した高度な文字列変換

フィールド数が多いクラスでは、文字列連結を手書きすると区切り文字や改行の管理が煩雑になるのが基本です。一方、Apache Commons LangのToStringBuilderを使うと、属性名と値を並べる形式を少ない記述で作れます。

依存関係の追加方法は、ビルドツールやバージョン方針で変わります。Mavenを使う場合はpom.xmldependencyを追加し、公式情報はApache Commons Langで確認できるのが目安です。

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency>
</dependencies>

結果: 期待される状態は、Mavenプロジェクトでcommons-lang3を参照できることです。実際のプロジェクトでは、組織の依存管理に合わせて利用可能なバージョンを選びます。

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class AdvancedPerson {
    private String name;
    private int age;
    private boolean isStudent;

    public AdvancedPerson(String name, int age, boolean isStudent) {
        this.name = name;
        this.age = age;
        this.isStudent = isStudent;
    }

    // toStringメソッドをオーバーライド
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
            .append("名前", name)
            .append("年齢", age)
            .append("学生かどうか", isStudent)
            .toString();
    }

    public static void main(String[] args) {
        AdvancedPerson person = new AdvancedPerson("Taro", 25, true);
        System.out.println(person.toString());
    }
}

結果: 期待される出力は、クラス名と各フィールドが複数行で並ぶ形式です。ToStringStyle.MULTI_LINE_STYLEを使うと、長いオブジェクトでも縦に読みやすい表示になります。

AdvancedPerson@abcdef[
  名前=Taro,
  年齢=25,
  学生かどうか=true
]

結果: このブロックは期待される出力例です。@abcdefに相当する部分はインスタンスごとに変わるため、完全一致のテストには向きません。

外部ライブラリによる応用例は、手書きの連結よりも形式を統一しやすい点に価値があります。ただし、依存関係を増やす判断には保守コストも伴うため、小さなクラスでは標準APIだけで足りる場合があるのがポイントです。

その確認では、戻り値が安定しているかも見ます。ランダム値、現在時刻、外部サービスの応答などを含めると、同じオブジェクトでも表示が変わり、ログ比較やテストの読み取りが難しくなります。

基本的に、toStringメソッドはオブジェクトが保持している状態を説明するためのものです。呼び出すたびに新しい情報を取りに行く処理は避け、既存フィールドから短い文字列を組み立てる形に寄せますが、覚えておくと役立つでしょう。

注意点と対処法

toStringメソッドの注意点は、null、巨大データ、機密情報に集中します。どれも出力の見た目だけでなく、例外、処理時間、情報漏えいに関わるため、サンプルコードの段階から対処法を含めて書くと安全です。

これらの注意点は、コードレビューでも見落とされやすい部分です。toStringメソッドは明示的に呼ばなくても、ログフレームワーク、デバッガ、文字列連結、例外処理の周辺で呼ばれることがあるため、意図しない出力が起きる前提で設計します。

そのため、オブジェクトの状態を詳しく出したい場面でも、ログレベルや出力先を意識する必要があるのが一般的です。開発中の詳細ログと本番運用の監査ログでは求められる粒度が異なるため、toStringメソッドへすべての責務を集めないほうが保守しやすくなります。

⚠️ 注意: toStringメソッドはログに出る可能性があります。パスワード、トークン、個人番号、秘密鍵などを返す文字列へ含めない設計が必要です。

null値を持つオブジェクトの取り扱い

Javaでは、参照がnullの変数に対してstr.toString()を呼ぶとNullPointerExceptionが発生するのが現実的です。この注意点は初心者がつまずきやすいため、呼び出し前の判定やString.valueOfの利用を検討します。

public class NullHandling {
    public static void main(String[] args) {
        String str = null;
        String result = (str == null) ? "null" : str.toString();
        System.out.println("結果:" + result);
    }
}

結果: 期待される出力は結果:nullです。三項演算子でnullを先に判定しているため、str.toString()が呼ばれず例外を避けられます。

その対処法として、ログ向けの簡易変換ではString.valueOf(str)も候補になると整理できます。nullを文字列の"null"へ変換する仕様があるため、明示的な条件分岐を減らせます。

null対策では、呼び出し側で判定する方法と、フィールドを保持するクラス側で未設定値を吸収する方法があります。どちらを選ぶかは責務の置き場所によって変わりますが、少なくともtoStringメソッド自体が簡単に例外を投げないようにする発想が必要です。

一方、すべてのnullを文字列の"null"へ変換すると、未設定なのか文字列としてのnullなのかが曖昧になる場合があると理解できます。ログで区別したい場合は、<未設定>N/Aのように、プロジェクト内で意味を決めた表記を使います。

パフォーマンス上の考慮事項

大きなリストや深いオブジェクトグラフをすべて文字列化すると、ログ量と処理時間が膨らむ可能性があります。そのため、toStringメソッドには件数上限、要約表示、遅延評価の考え方を組み込むと扱いやすくなると覚えるとよいでしょう。

public class PerformanceExample {
    private String someLargeString;
    private List<String> someLargeList;

    @Override
    public String toString() {
        if (someLargeList.size() > 100) {
            return "Data is too large to print";
        }
        return "PerformanceExample [someLargeString=" + someLargeString + ", someLargeList=" + someLargeList + "]";
    }
}

結果: 期待される表示は、someLargeListの件数が100を超える場合にData is too large to printとなることです。件数が少ない場合だけ詳細を返すため、ログの肥大化を抑えられます。

ただし、このサンプルコードではsomeLargeList自体がnullの場合の判定を省略しています。実装パターンとしてよく見るのは、someLargeList == nullを先に確認し、未設定状態を別の文字列で返す形です。

セキュリティ上の注意点

ログや例外メッセージは、開発者だけでなく運用基盤や監視サービスへ送られる場合があると考えられます。このとき、toStringメソッドが機密情報を返すと、意図しない場所へ値が残るリスクがあります。

public class SecurityRisk {
    private String username;
    private String password;

    @Override
    public String toString() {
        return "Username: " + username + ", Password: [REDACTED]";
    }
}

結果: 期待される表示は、usernameだけが出力され、password[REDACTED]として隠される形です。機密値を持つフィールドは、表示しない、マスクする、末尾だけ見せるなどの方針を事前に決めます。

関連する文字列の扱いでは、エスケープやログ出力の知識も役立ちます。特殊文字の扱いはJavaエスケープ処理の解説、日付や条件分岐の基礎はJavaでうるう年を判定する解説と合わせて確認できると言えるでしょう。

機密情報を隠す設計では、値を持つクラスのtoStringメソッドだけでなく、周辺クラスの表示も確認します。たとえばユーザー情報を含むセッション、リクエスト、監査イベントが同じオブジェクトを参照している場合、どこかひとつの表示から漏れる可能性があります。

そのため、パスワードやトークンを表す型では、初めからtoStringメソッドで値を返さない方針にしておくと安全です。文字列化が必要な場面でも、マスク済みの値だけを返す専用メソッドへ限定するのが基本です。

カスタマイズ方法

カスタマイズ方法では、どの形式で返すかを先に決めます。人間が読むログなら短いラベル付き文字列、構造化データに近づけたいならJSON風、複数オブジェクトを束ねるならStringBuilderで区切りを管理する形が候補になります。

具体的には、表示形式のカスタマイズは「誰が読むか」「どの場面で読むか」「どの値を隠すか」を決めてから書きますし、ここを基本と考えるとよいでしょう。人間向けならラベルを含め、機械処理向けなら専用のシリアライザへ任せ、ログ向けなら長さと機密性を制御します。

使い分けると、toStringメソッドは診断用、JSON変換はGsonや別ライブラリ、画面表示はView層のフォーマッタという役割分担になります。役割が分かれるほど、カスタマイズの影響範囲を小さく保てますし、ここがポイントです。

ℹ️ 補足: JSONとして外部へ渡す目的なら、手作業で{}を連結するより、Gsonなどのライブラリでエスケープ処理を任せるほうが堅実です。

サンプルコード9:toStringメソッドの挙動をカスタマイズする

JSON風の文字列を返すカスタマイズは、ログに構造を持たせたいときに使われます。このサンプルコードではPersonnameageを波括弧の中へ並べます。

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "{"name": "" + name + "", "age": " + age + "}";
    }

    public static void main(String[] args) {
        Person person = new Person("Taro", 30);
        System.out.println(person);
    }
}

結果: 期待される出力は{"name": "Taro", "age": 30}です。ただし、この形式は簡易的な表示であり、文字列内の引用符や改行まで正しく扱う本格的なJSON生成には向きません。

JSON風のカスタマイズは見た目が分かりやすい一方、文字列値に引用符、円記号、改行が含まれると崩れやすくなります。手作業の連結は短い学習例にとどめ、データ交換に使う場合はシリアライズ専用APIへ切り替えるのが安全です。

この判断は、toStringメソッドの責務を狭く保つことにもつながります。診断用の表示と外部連携用の形式を分けることで、ログの読みやすさを変えてもAPIレスポンスや保存データへ影響しにくくなるのが目安です。

サンプルコード10:外部ライブラリを使用したカスタマイズ

JSON文字列として扱うなら、GsonのtoJsonに変換を任せる方法があります。公式情報はGsonのGitHubリポジトリで確認でき、依存関係を追加したうえで利用します。

import com.google.gson.Gson;

public class PersonWithGson {
    private String name;
    private int age;

    public PersonWithGson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        PersonWithGson person = new PersonWithGson("Taro", 30);
        Gson gson = new Gson();
        String json = gson.toJson(person);
        System.out.println(json);
    }
}

結果: 期待される出力は{"name":"Taro","age":30}です。toString()を無理にJSON生成へ使わず、専用APIへ役割を分けるとエスケープや型変換の抜けを減らせます。

サンプルコード11:toStringメソッドの拡張利用例

toStringメソッドの戻り値を比較に使う応用例もあります。もっとも、並び順のルールを文字列表現へ強く結び付けると、表示形式の変更がソート結果まで変えるため、学習用の例として読むのが適しているのがポイントです。

import java.util.Arrays;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + "-" + age;
    }

    @Override
    public int compareTo(Person o) {
        return this.toString().compareTo(o.toString());
    }

    public static void main(String[] args) {
        Person[] persons = {
            new Person("Alice", 25),
            new Person("Bob", 20),
            new Person("Catherine", 22)
        };

        Arrays.sort(persons);

        for (Person person : persons) {
            System.out.println(person);
        }
    }
}

結果: 期待される出力は、名前と年齢をハイフンで結合した文字列が辞書順に並ぶ形です。実務的にはComparator.comparingnameなどの比較対象を明示するほうが、表示変更の影響を受けにくくなります。

Alice-25
Bob-20
Catherine-22

結果: このブロックは期待される出力例です。Arrays.sortcompareToを使い、toString()の文字列比較に基づいて順序を決めます。

比較やソートへ応用する場合、表示用の文字列がそのまま順序のルールになるのが一般的です。名前順で並べたいだけなら分かりやすい例ですが、年齢順、作成日順、優先度順など複数の基準がある場合は、ComparableよりComparatorを使い分ける設計が自然です。

とはいえ、toStringメソッドの戻り値を比較へ使う例は、文字列表現がプログラムの挙動に影響することを理解する助けになります。表示形式を変更すると並び順も変わるため、診断用の文字列と業務上の比較条件を分離する理由が見えてきます。

サンプルコード12:複数のオブジェクトとの組み合わせでのカスタマイズ

複数のオブジェクトをまとめて表示する場合は、StringBuilderで区切り文字を制御するのが現実的です。このカスタマイズでは、各Personの文字列表現を再利用しながら、チーム全体の一行表示を作ります。

public class Team {
    private Person[] members;

    public Team(Person[] members) {
        this.members = members;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Team members: ");

        for (int i = 0; i < members.length; i++) {
            sb.append(members[i].toString());
            if (i < members.length - 1) {
                sb.append(", ");
            }
        }

        return sb.toString();
    }

    public static void main(String[] args) {
        Person[] persons = {
            new Person("Alice", 25),
            new Person("Bob", 20),
            new Person("Catherine", 22)
        };
        Team team = new Team(persons);
        System.out.println(team);
    }
}

結果: 期待される出力は、Team members: に続いて複数のPersonがカンマ区切りで並ぶ形です。配列の長さを見ながら区切りを追加するため、末尾に余分なカンマが付きません。

Team members: Alice-25, Bob-20, Catherine-22

結果: このブロックは期待される出力例です。各メンバーの表示はPerson#toStringへ委ねられるため、メンバー側のカスタマイズがチーム表示にも反映されます。

このとき、StringBuilderはループ内で文字列を組み立てる用途に向いています。短い固定文字列なら+連結でも読みやすいですが、要素数が変わる配列やリストでは、appendで順に積み上げるほうが区切り制御を表現しやすくなると整理できます。

逆に、複数オブジェクトを連結した結果を後続処理の入力にするなら、toStringメソッドの戻り値へ依存しない設計を選びます。表示向けの文言変更が処理結果へ影響しないよう、IDの配列やDTOなど別の形でデータを渡すほうが現実的です。

同様に、アノテーションで表示対象を制御する設計もあります。注釈の基礎を押さえる場合はJavaアノテーションの解説が関連すると理解できます。

まとめ

JavaのtoStringメソッドは、オブジェクトを人間が読める文字列へ変えるための入口です。既定表示はクラス名と識別用の値に近い情報を返すため、フィールドの意味まで伝えたいクラスでは@Overrideによるカスタマイズが役立ちます。

その使い方は、基本的なフィールド連結から、リスト、日付、条件分岐、外部ライブラリ、複数オブジェクトの連結まで広がります。一方で、null参照、巨大データ、機密情報という注意点を見落とすと、例外やログ肥大化、情報漏えいにつながる可能性があると覚えるとよいでしょう。

具体的には、サンプルコードのようにNullPointerExceptionを避ける判定、出力件数の制限、[REDACTED]によるマスクを組み込みます。JSONが必要な場合はGsonのような専用ライブラリへ任せ、toStringメソッドは読みやすい診断用の文字列表現に保つと運用しやすくなります。

基本的に、良いtoStringメソッドは短く、例外を起こしにくく、機密情報を含まず、オブジェクトの識別に必要な情報を返すると考えられます。サンプルコードを自分のクラスへ置き換えるときは、全フィールドを並べる前に、調査時に本当に必要な値を選びます。

そのうえで、表示形式を頻繁に変える可能性があるなら、テストでは完全一致よりも主要な値が含まれるかを確認する方法が合う場合があります。toStringメソッドは利用者に見せる契約というより、保守しやすい診断情報として扱うと、変更への負担を抑えられますが、これは押さえたい点です。

これらを踏まえると、toStringメソッドのサンプルコードは、単に書き方を覚えるためだけでなく、クラス設計の境界を考える材料にもなります。表示、変換、比較、保存を混同しないことが、あとから変更しやすいコードにつながります。

この方針にすると、クラスを追加したときの判断も揃いると言えるでしょう。新しいフィールドを必ず出すのではなく、識別や状態確認に必要な値だけを選び、長い説明は別のログメッセージへ分けます。

その積み重ねが、ログの読みやすさとクラス責務の分離を両立させます。

この基準を持つと、サンプルコードを実案件へ移すときも判断がぶれにくくなるのが基本です。

その結果、変更にも追随しやすい設計になります。

関連記事

著者: Japanシーモア編集部

Japanシーモアは、Web/IoT/APP/SYS 分野のプログラミング情報を体系的に提供するメディアです。本記事は編集部による執筆とAI支援を組み合わせて制作し、公開前に編集部が校正しています。誤りや改善案がございましたらお問い合わせよりご連絡ください。

※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。