web-dev-qa-db-ja.com

lombokビルダーを使用して継承関係があるときにオブジェクトを構築するにはどうすればよいですか?

私のプロジェクトでは、クラスのゲッターとセッターの作成を回避するためにロンボクを使用しています。また、新しいObeject()を記述してすべての値を設定する代わりに、lombok.Builderを使用してオブジェクトを構築しています。

しかし、継承関係があり、lombokビルダーを使用して子オブジェクトを構築したい場合、親のフィールドを取得できません。

例えば:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class Parent{
  private String nationality;
  .
  .
  // more columns
}

そして、Childクラスは次のようになります。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Child extends Parent{
   private String firstName;
   private String lastName;
   .
   .
}

子オブジェクトを作成する必要がある私のテストクラス

public class Test{

 public void testMethod(){
   Child child = Child.builder()
            .firstName("Rakesh")
            .lastName("SS")
            .nationality("some text")// I am not able to set nationality               
            .build();
 }


}

ロンボクでこのシナリオを処理する方法はありますか?.

14
Rakesh

@Builderには、公開したいParentのフィールドを判別する方法がありません。

@Builderがクラスに配置されると、そのクラスで明示的に宣言されたフィールドのみが*Builderに追加されます。

@Builderが静的メソッドまたはコンストラクターに配置されると、結果の*Builderには各引数のメソッドが含まれます。

また、@Builderを使用している場合、少なくともChildは不変であることを想定していると想定しても安全ですか?

Parentが可変でChildが不変である例と、ParentChildの両方が不変である例の2つを示します。

不変の親子

import static org.junit.Assert.*;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;
import lombok.experimental.NonFinal;

import org.junit.Test;

public class So32989562ValueTest {

    @Value
    @NonFinal
    public static class Parent {

        protected final String nationality;

    }

    @Value
    @ToString(callSuper = true)
    @EqualsAndHashCode(callSuper = true)
    public static class Child extends Parent {

        private final String firstName;

        private final String lastName;

        @Builder(toBuilder = true)
        private Child(String nationality, String firstName, String lastName) {
            super(nationality);
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }

    @Test
    public void testChildBuilder() {

        String expectedFirstName = "Jeff";
        String expectedLastName = "Maxwell";
        String expectedNationality = "USA";

        Child result = Child.builder()
            .firstName(expectedFirstName)
            .lastName(expectedLastName)
            .nationality(expectedNationality)
            .build();

        assertEquals(result.toString(), expectedFirstName, result.getFirstName());
        assertEquals(result.toString(), expectedLastName, result.getLastName());
        assertEquals(result.toString(), expectedNationality, result.getNationality());
    }
}

可変親、不変子:

import static org.junit.Assert.*;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;

import org.junit.Test;

public class So32989562DataTest {

    @Data
    public static class Parent {

        protected String nationality;

    }

    @Value
    @ToString(callSuper = true)
    @EqualsAndHashCode(callSuper = true)
    public static class Child extends Parent {

        private final String firstName;

        private final String lastName;

        @Builder(toBuilder = true)
        private Child(String nationality, String firstName, String lastName) {
            this.setNationality(nationality);
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }

    @Test
    public void testChildBuilder() {

        String expectedFirstName = "Jeff";
        String expectedLastName = "Maxwell";
        String expectedNationality = "USA";

        Child result = Child.builder()
            .firstName(expectedFirstName)
            .lastName(expectedLastName)
            .nationality(expectedNationality)
            .build();

        assertEquals(result.toString(), expectedFirstName, result.getFirstName());
        assertEquals(result.toString(), expectedLastName, result.getLastName());
        assertEquals(result.toString(), expectedNationality, result.getNationality());
    }
}
19
Jeff

上記の解決策は機能しますが、あまりにも多くの回避策が必要です。さらに、子クラスと親クラスを変更するには、どこでもコンストラクタの引数を変更する必要があります。

Lombokは実験的機能をバージョン1.18.2で導入し、Builderアノテーションで直面する継承の問題に対応し、@ SuperBuilderアノテーションで以下のように解決できます。

@SuperBuilder
public class ParentClass {
    private final String a;
    private final String b;
}

@SuperBuilder
public class ChildClass extends ParentClass{
    private final String c;
}

これで、次のようにBuilderクラスを使用できます(@Builderアノテーションでは不可能でした)。

ChildClass.builder().a("testA").b("testB").c("testC").build();
9
Amit Kaneria