ロンボクの@ToStringアノテーション
1. 概要
ご存知のように、 toString()メソッドは、Javaオブジェクトの文字列表現を取得するために使用されます。
Project Lombok は、定型文やソースコードを乱雑にすることなく、一貫性のある文字列表現を生成するのに役立ちます。 また、特にクラスに多数のフィールドが含まれる可能性がある場合は、保守性を向上させることができます。
このチュートリアルでは、このメソッドを自動生成する方法と、結果の出力をさらに微調整するために使用できるさまざまな構成オプションについて説明します。
2. 設定
サンプルプロジェクトにProjectLombok依存関係を含めることから始めましょう。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
この例では、いくつかのフィールドを持つ単純な Account POJOクラスを使用して、機能とさまざまな構成オプションを示します。
3. 基本的な使用法
Lombok @ToStringアノテーションを使用して任意のクラスにアノテーションを付けることができます。 これにより、生成されたバイトコードが変更され、 toString()メソッドの実装が作成されます。
この注釈を単純なアカウントPOJOに適用してみましょう。
@ToString
public class Account {
private String id;
private String name;
// standard getters and setters
}
デフォルトでは、 @ToStringアノテーションは、各非静的フィールド名と、getter(宣言されている場合)を呼び出すことによって取得されたその値とともにクラス名を出力します。 フィールドは、ソースクラスの宣言順序に従っても表示されます。 異なるフィールド値のペアはコンマで区切ります。
ここで、このクラスのインスタンスで toString()メソッドを呼び出すと、次の出力が生成されます。
Account(id=12345, name=An account)
ほとんどの場合、これはJavaオブジェクトの標準的で有用な文字列表現を生成するのに十分です。
4. 構成オプション
生成されたtoString()メソッドを変更および微調整できるいくつかの構成オプションを使用できます。 これらは、特定のユースケースで役立ちます。 これらをもう少し詳しく見てみましょう。
4.1. スーパークラスtoString()
デフォルトでは、出力には toString()メソッドのスーパークラス実装からのデータは含まれていません。 ただし、これを変更するには、callSuper属性値を true:に設定します。
@ToString(callSuper = true)
public class SavingAccount extends Account {
private String savingAccountId;
// standard getters and setters
}
これにより、スーパークラス情報とそれに続くサブクラスフィールドおよび値を含む次の出力が生成されます。
SavingAccount(super=Account(id=12345, name=An account), savingAccountId=6789)
重要なことに、これはjava.lang.Object以外のクラスを拡張する場合にのみ本当に有益です。 toString()の Object 実装は、あまり有用な情報を提供しません。 つまり、このデータを含めると、冗長な情報が追加されるだけでなく、出力の冗長性が高まります。
4.2. フィールド名の省略
前に見たように、デフォルトの出力にはフィールド名とそれに続く値が含まれます。 ただし、@ ToStringアノテーションでincludeFieldNames属性をfalseに設定することにより、出力からフィールド名を省略できます。
@ToString(includeFieldNames = false)
public class Account {
private String id;
private String name;
// standard getters and setters
}
その結果、出力には、フィールド名を含まないすべてのフィールド値のコンマ区切りのリストが表示されます。
Account(12345, An account)
4.3. ゲッターの代わりにフィールドを使用する
すでに見てきたように、getterメソッドは印刷用のフィールド値を提供します。 さらに、クラスに特定のフィールドのgetterメソッドが含まれていない場合、Lombokはフィールドに直接アクセスし、その値を取得します。
ただし、 doNotUseGetters属性をtrueに設定することで、ゲッターではなく常に直接フィールド値を使用するようにLombokを構成できます。
@ToString(doNotUseGetters = true)
public class Account {
private String id;
private String name;
// ignored getter
public String getId() {
return "this is the id:" + id;
}
// standard getters and setters
}
この属性がないと、ゲッターを呼び出して取得した出力が得られます。
Account(id=this is the id:12345, name=An account)
代わりに、 doNotUseGetters属性を使用すると、getter を呼び出さずに、出力に実際にidフィールドの値が表示されます。
Account(id=12345, name=An account)
4.4. フィールドの包含と除外
パスワード、その他の機密情報、大きなJSON構造など、特定のフィールドを文字列表現から除外したいとします。 @ ToString.Excludeアノテーションでアノテーションを付けるだけで、このようなフィールドを省略できます。
nameフィールドを表現から除外しましょう。
@ToString
public class Account {
private String id;
@ToString.Exclude
private String name;
// standard getters and setters
}
または、出力で必要なフィールドのみを指定できます。 これを実現するには、クラスレベルで @ToString(onlyExplicitlyIncluded = true)を使用し、必要な各フィールドに @ToString.Includeという注釈を付けます。
@ToString(onlyExplicitlyIncluded = true)
public class Account {
@ToString.Include
private String id;
private String name;
// standard getters and setters
}
上記の両方のアプローチは、idフィールドのみで次の出力を生成します。
Account(id=12345)
さらに、 Lombok出力は、$記号で始まる変数を自動的に除外します。 ただし、フィールドレベルで @ ToString.Include アノテーションを追加することで、この動作をオーバーライドして含めることができます。
4.5. 注文出力
デフォルトでは、出力にはクラス内の宣言順序に従ったフィールドが含まれます。 ただし、 @ ToString.Includeアノテーションにrank属性を追加するだけで、順序を調整できます。
Account クラスを変更して、クラス定義の宣言位置に関係なく、idフィールドが他のフィールドの前にレンダリングされるようにします。 これを実現するには、 @ ToString.Include(rank = 1)アノテーションをidフィールドに追加します。
@ToString
public class Account {
private String name;
@ToString.Include(rank = 1)
private String id;
// standard getters and setters
}
これで、 id フィールドは、 name フィールドの後に宣言されているにもかかわらず、出力の最初にレンダリングされます。
Account(id=12345, name=An account)
出力には、最初に上位ランクのメンバーが含まれ、次に下位ランクのメンバーが含まれます。ランク属性のないメンバーのデフォルトのランク値は0です。 同じランクのメンバーは、宣言順に印刷されます。
4.6. メソッド出力
フィールドに加えて、引数をとらないインスタンスメソッドの出力を含めることもできます。 これを行うには、引数なしのインスタンスメソッドを@ToString.Includeでマークします。
@ToString
public class Account {
private String id;
private String name;
@ToString.Include
String description() {
return "Account description";
}
// standard getters and setters
}
これにより、 description がキーとして追加され、その出力が値としてAccount表現に追加されます。
Account(id=12345, name=An account, description=Account description)
指定されたメソッド名がフィールド名と一致する場合、そのメソッドはフィールドよりも優先されます。 つまり、出力には、一致するフィールド値ではなく、メソッド呼び出しの結果が含まれます。
4.7. フィールド名の変更
@ToString.Includeアノテーションのname属性に別の値を指定することで、任意のフィールド名を変更できます。
@ToString
public class Account {
@ToString.Include(name = "identification")
private String id;
private String name;
// standard getters and setters
}
これで、出力には、実際のフィールド名の代わりに、アノテーション属性からの代替フィールド名が含まれます。
Account(identification=12345, name=An account)
5. 配列の印刷
配列は、 Arrays.deepToString()メソッドを使用して出力されます。 これは、配列要素を対応する文字列表現に変換します。 ただし、配列に直接参照または間接循環参照が含まれている可能性があります。
無限再帰とそれに関連するランタイムエラーを回避するために、このメソッドは、配列への循環参照を「[[…]]」としてレンダリングします。
Object配列フィールドをAccountクラスに追加してこれを見てみましょう。
@ToString
public class Account {
private String id;
private Object[] relatedAccounts;
// standard getters and setters
}
relatedAccounts配列が出力に含まれるようになりました。
Account(id=12345, relatedAccounts=[54321, [...]])
重要なことに、循環参照は deepToString()メソッドによって検出され、 StackOverflowError を発生させることなく、Lombokによって適切にレンダリングされます。
6. 覚えておくべきポイント
予期しない結果を回避するために重要な、言及する価値のある詳細がいくつかあります。
クラスにtoString()という名前のメソッドが存在する場合(戻りタイプに関係なく)、LombokはtoString()メソッドを生成しません。
Lombokのバージョンが異なると、生成されたメソッドからの出力形式が変更される場合があります。 いずれの場合も、 toString()メソッドの出力の解析に依存するコードは避ける必要があります。 したがって、これは実際には問題にはならないはずです。
最後に、この注釈をenumsに追加することもできます。 これにより、enum値がenumクラス名の後に続く表現が生成されます(例: AccounType.SAVING )。
7. 結論
この記事では、Lombokアノテーションを使用して、最小限の労力と定型文で[X102X]オブジェクトのString表現を生成する方法を説明しました。
最初に、基本的な使用法を検討しました。これは通常、ほとんどの場合に十分です。 次に、生成された出力を微調整および調整するために使用できるさまざまなオプションについて説明しました。
いつものように、完全なソースコードはGitHubでから入手できます。