概要
Lombokの基礎については、すでにたくさんの説明ページがあるので、Lombokの活用方法について主にJUnitを中心に記載していく.
記載対象のアノテーション
@EqualsHashcode
@ToString
-
@Data
の功罪
@EqualsHashCode
について
このアノテーションを付与すると、boolean:equals(Object)
と int hashCode()
が自動生成される。
どちらもObjectクラスのメソッドのオーバーライドとなる.
equals
で作成されるコードは、ざっくりいうと配下のフィールドをすべて比較してすべて同一であったらtrueを返却する.
細かく書くと
以下順に精査を行う 1. 引数のNullチェック 2. 引数が同じクラスで作成されたインスタンスか? 3. (この精査は別途`protected boolean canEqual(Object other)`というメソッドが作成されるので、継承先でoverride可能) 3. フィールドがすべて同じか?JUnitでの用途
@EqualsAndHashCode
を実装すると、JUnitを行う際に、フィールドについて精査を行ってくれるので作成インスタンス自体に精査を行えば事足りる
(その後フィールドの増減が発生した際にもequals(Object)
書き換わるので精査し忘れを防ぐことができる)
EqualsHashCodeの注意点
あるクラスAを継承したクラスBに対して、@EqualsHashCode
を付与した場合、そのままだとB配下のフィールドしか判定対象とならず、Aが保持しているフィールドは判定対象にならない.
@EqualsAndHashCode
@Setter
@Getter
class A {
private String aField
}
@Setter
@Getter
@EqualsAndHashCode
class B extends A {
private String bField
}
とクラスBを定義した場合
@org.junit.Test
public void test(){
BDto b1 = new BDto();
b1.setBField("b1");
b1.setAField("a1");
BDto b2 = new BDto();
b2.setBField("b1");
b2.setAField("aXXXXXXXXXX"); //b1と異なる値を設定
assertEquals(b2,b1);
}
上記テストは通ってしまう(本来はaField
の値が異なるのでエラーとしたいのに)
そのため、とあるクラスを継承した場合は@EqualsAndHashCode(callSuper = true)
と指定する。
ただし継承元のクラスがequals
を継承している場合に限る
上記の場合は、Bの定義を以下のように書き換えると正しく判定を行えるようになる
@Setter
@Getter
@EqualsAndHashCode(callSuper = true)
class B extends A {
private String bField
}
@ToString
について
JUnitでの用途
@ToString
を付与しないクラスの場合、下記テストでエラーになっても
@org.junit.Test
public void test(){
BDto b1 = new BDto();
b1.setBField("b1");
b1.setAField("a1");
BDto b2 = new BDto();
b2.setBField("b1");
b2.setAField("aXXXXXXXXXX"); //b1と異なる値を設定
assertEquals(b2,b1);
}
java.lang.AssertionError:
Expected :BDto@81666743
Actual :BDto@2d9f8
となって、何が悪いか全くわからない
その対応として@ToString
を設定すると何が悪いかわかるようになるが、@EqualsAndHashCode
と同様とあるクラスを継承した場合は(callSuper = true)
をつけるのを忘れないようにすること
(callSuper = true)
つけなかった場合の出力例(@ToString
とだけ指定)
java.lang.AssertionError: expected: BDto<BDto(bField=b1)> but was: B<B(bField=b1)>
Expected :B<B(bField=b1)>
Actual :B<B(bField=b1)>
継承元の値が出力されないので何が悪いかわからない
(callSuper = true)
つけた場合何が悪いか一目瞭然となる(@ToString()
と指定)
java.lang.AssertionError:
Expected :B(super=A(aField=aXXXXXXXXXX), bField=b1)
Actual :B(super=A(aField=a1), bField=b1)
クラスAとBの定義はこのような形となる
継承元
@EqualsAndHashCode
@Getter
@Setter
@ToString
public class A {
private String aField;
}
継承先
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class B extends A {
private String bField;
}
@Data
の功罪
功
@Data
を付与すると以下アノテーションを付与したのと同じになるので、楽に記載できる
@ToString
@Getter
@Setter
@RequiredArgsConstructor
@EqualsAndHashCode
罪
上記の指定を一括で行ってくれるが、何もわからず使ってしまうと(callSuper = true)
の設定ができないので、意図した実装にならない可能性があるので注意が必要