覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Lombok - @ToString

Lombokの@ToString(lombok.ToString)アノテーションの利用サンプルです。

@ToStringアノテーションを付与することで、toStringメソッドを適切にオーバーライドしてくれます。
アノテーションにパラメータを与えることでtoStringメソッドでの文字列化処理や文字列化対象について、制御を行うことができます。

ちなみに、@Data@Valueのアノテーションを付与すると、内部的に@ToStringアノテーションが付与されたのと同じことが起こります。


@ToStringの文字列出力はどんな動きか?

delombokすればいいだけのことですが、実際にコードを動かして確認してみます

■@ToStringアノテーションを付与したクラス

  • 以下のようなクラスを用意しました
    • @ToStringアノテーションを付与
    • @Getter, @Setterアノテーションは使用しない
    • getter/setter を自前で記述
    • getterメソッドのないフィールドが存在する
    • 配列のフィールドが存在する
    • フィールドの値を装飾して返すgetterが存在する
    • prefixが"get"ではないアクセサを持つフィールドが存在する
    • staticフィールドを配置
import lombok.ToString;

@ToString
public class Person1 {

    // staticフィールド
    private static final String DEFALUT_REMARKS = "none";

    // getter無し
    private long id;

    // getterでは装飾した文字列が返される
    private String name;

    private int age;

    // 配列のメンバ
    private String[] memos;

    // prefixが"get"ではないアクセサを持つ
    private String remarks = DEFALUT_REMARKS;



    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return "####" + name + "#####";
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String[] getMemos() {
        return memos;
    }

    public void setMemos(String[] memoArray) {
        this.memos = memoArray;
    }

    public String remarks() {
        return "備考…" + remarks;
    }

    public void remarks(String remarks) {
        this.remarks = remarks;
    }
}

■利用側のコード

toStringメソッドの呼び出しとgetterメソッドの呼び出しを行っています。

public class Person1Client {

    public static void main(String[] args) {
        Person1 p = new Person1();
        p.setId(10L);
        p.setName("Kernel Taro");
        p.setAge(55);
        p.setMemos(new String[] {"memo1", "memo2", "memo3"});
        p.remarks("逮捕歴無し");

        System.out.println(p);

        System.out.println("----------");

        System.out.println("p#getName: " + p.getName());
        System.out.println("p#getAge: " + p.getAge());
        System.out.println("p#getMemos: " + p.getMemos());
        System.out.println("p#remarks: " + p.remarks());
    }
}

■実行結果

Person1(id=10, name=####Kernel Taro#####, age=55, memos=[memo1, memo2, memo3], remarks=逮捕歴無し)
----------
p#getName: ####Kernel Taro#####
p#getAge: 55
p#getMemos: [Ljava.lang.String;@79f1d448
p#remarks: 備考…逮捕歴無し

実行結果から見ても分かることは以下です

  • toStringの処理内では各メンバの値取得はgetterを通して行われる
  • ただし、getterメソッドなければ直接メンバの値が使用される
  • アクセサの名前の先頭が"get"でなければ直接メンバの値が使用される
  • 配列の値は適切に文字列化処理が行われる
  • staticメンバはtoStringの対象にならない

■実際に生成されているコード

実際に生成されているソースコードをdelombokを使って確認してみました

public class Person1 {
    private static final String DEFALUT_REMARKS = "none";
    private long id;
    private String name;
    private int age;
    private String[] memos;
    private String remarks = DEFALUT_REMARKS;

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return "####" + name + "#####";
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String[] getMemos() {
        return memos;
    }

    public void setMemos(String[] memoArray) {
        this.memos = memoArray;
    }

    public String remarks() {
        return "備考…" + remarks;
    }

    public void remarks(String remarks) {
        this.remarks = remarks;
    }

    @Override
    public String toString() {
        return "Person1(id=" + this.id + ", name=" + this.getName() + ", age=" + this.getAge() + ", memos=" + java.util.Arrays.deepToString(this.getMemos()) + ", remarks=" + this.remarks + ")";
    }
}


デフォルトの@ToStringアノテーション

対象クラスに@ToStringアノテーションを付与します。パラメータの指定はしません

■@ToStringアノテーションを付与したクラス

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Account1 {

    private String id;

    private String password;

    private String name;
}

■利用側のコード

public class Account1Client {

    public static void main(String[] args) {
        Account1 account = new Account1();
        account.setId("abc-0001");
        account.setPassword("pass-012345");
        account.setName("LombokJiro");

        System.out.println("Account1#toString: " + account);
    }
}

■実行結果

Account1#toString: Account1(id=abc-0001, password=pass-012345, name=LombokJiro)


特定のメンバをtoStringの対象から除外する

セキュリティ的な問題や諸々の理由で特定のメンバをtoStringの結果に含めたくない場合があります。
その場合は excludeパラメータに除外対象のフィールド名を指定します

■@ToStringアノテーションを付与したクラス

@Getter
@Setter
@ToString(exclude="password")
public class Account2 {

    private String id;

    private String password;

    private String name;
}

■利用側のコード

public class Account2Client {

    public static void main(String[] args) {
        Account2 account = new Account2();
        account.setId("abc-0001");
        account.setPassword("pass-012345");
        account.setName("LombokJiro");

        System.out.println("Account2#toString: " + account);
    }
}

■実行結果

Account2#toString: Account2(id=abc-0001, name=LombokJiro)


toStringの結果にフィールド名を含めない

デフォルトでは@ToStringアノテーションを付与するとtoStringの結果には各フィールド名も出力されます
このフィールド名が不要という場合には、includeFieldNames=falseの指定を行うことで実現できます。

■@ToStringアノテーションを付与したクラス

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString(includeFieldNames=false)
public class Account3 {

    private String id;

    private String password;

    private String name;
}

■利用側のコード

public class Account3Client {

    public static void main(String[] args) {
        Account3 account = new Account3();
        account.setId("abc-0001");
        account.setPassword("pass-012345");
        account.setName("LombokJiro");

        System.out.println("Account3#toString: " + account);
    }
}

■実行結果

Account3#toString: Account3(abc-0001, pass-012345, LombokJiro)


継承クラスにおける@ToStringアノテーションの動作

デフォルトでは、@ToStringアノテーションを付与したクラスのtoStringは親クラスのtoStringの呼び出しを行いません。
親クラスのtoStringの呼び出しを行いたい場合は、callSuper=trueを指定します。

■@ToStringアノテーションを付与したクラス

  • Account4a・・・デフォルトの@ToStringアノテーション付与
  • Account4b・・・@ToStringアノテーションにcallSuper=trueの指定あり
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Account4 {

    private String id;

    private String password;

    private String name;


    @Getter
    @Setter
    @ToString
    public static class Account4a extends Account4 {

        private String remarks;
    }

    @Getter
    @Setter
    @ToString(callSuper=true)
    public static class Account4b extends Account4 {

        private String remarks;
    }
}

■利用側のコード

public class Account4Client {

    public static void main(String[] args) {
        Account4a account1 = new Account4a();
        account1.setId("abc-0001");
        account1.setPassword("pass-012345");
        account1.setName("LombokJiro");
        account1.setRemarks("備考です001");

        System.out.println("Account4a#toString: " + account1);


        Account4b account2 = new Account4b();
        account2.setId("abc-0001");
        account2.setPassword("pass-012345");
        account2.setName("LombokJiro");
        account2.setRemarks("備考です001");

        System.out.println("Account4b#toString: " + account2);
    }
}

■実行結果

Account4a#toString: Account4.Account4a(remarks=備考です001)
Account4b#toString: Account4.Account4b(super=Account4(id=abc-0001, password=pass-012345, name=LombokJiro), remarks=備考です001)


toStringでgetterを利用しないようにする

デフォルトの@ToStringアノテーションでは、先の例に示した通りgetterメソッドを通じて各メンバへアクセスが行われます。
getterを利用してほしくない場合は、doNotUseGetters=trueを指定します。

■@ToStringアノテーションを付与したクラス

import lombok.Setter;
import lombok.ToString;

@Setter
@ToString(doNotUseGetters=true)
public class Account5 {

    private String id;

    private String password;

    private String name;


    public String getId() {
        return id;
    }

    public String getPassword() {
        return password;
    }

    // 装飾した文字列を返す
    public String getName() {
        return "#### " + name + " ####";
    }
}

■利用側のコード

public class Account5Client {

    public static void main(String[] args) {
        Account5 account = new Account5();
        account.setId("abc-0001");
        account.setPassword("pass-012345");
        account.setName("LombokJiro");

        System.out.println("Account5#toString: " + account);
    }
}

■実行結果

Account5#toString: Account5(id=abc-0001, password=pass-012345, name=LombokJiro)


toStringに含めるメンバを明示的に指定する

excludeとは逆にofを利用することでtoStringの結果に含めるメンバを明示的に指定することができます

■@ToStringアノテーションを付与したクラス

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString(of={"id", "name"})
public class Account6 {

    private String id;

    private String password;

    private String name;
}

■利用側のコード

public class Account6Client {

    public static void main(String[] args) {
        Account6 account = new Account6();
        account.setId("abc-0001");
        account.setPassword("pass-012345");
        account.setName("LombokJiro");

        System.out.println("Account6#toString: " + account);
    }
}

■実行結果

Account6#toString: Account6(id=abc-0001, name=LombokJiro)


@Data + @ToString

@Dataアノテーションと@ToStringアノテーションを同時に付与して@ToStringにパラメータを指定した場合、
@ToStringに指定したパラメータは有効に働きます

■@Dataと@ToStringアノテーションを付与したクラス

import lombok.Data;
import lombok.ToString;

@Data
@ToString(exclude="password")
public class Account7 {

    private String id;

    private String password;

    private String name;
}

■利用側のコード

public class Account7Client {

    public static void main(String[] args) {
        Account7 account = new Account7();
        account.setId("abc-0001");
        account.setPassword("pass-012345");
        account.setName("LombokJiro");

        System.out.println("Account7#toString: " + account);
    }
}

■実行結果

Account7#toString: Account7(id=abc-0001, name=LombokJiro)



関連エントリ