'Why doesn't Lombok Builder work with mutating an existing object?

Why doesn't Lombok Builder work with mutating an existing object?

What if I want to create a partially build object, and then later on update the object with the remaining fields?

Notice that this is not working. The pre-compiler doesn't like what I am trying to do.

enter image description here

Code Provided:

private static final ObjectMapper objectMapper = new ObjectMapper();

{
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
@Test
public void testBuilderMutating() throws JsonProcessingException {
    Book unfinishedBook = Book.builder()
            .isbn(String.valueOf(UUID.randomUUID()))
            .title("Title")
            .genre("Science Fiction")
            .author("me")
            .published(LocalDateTime.now())
            .description("description ...")
            .build();
    Book completedBook = rebuildBook(unfinishedBook)
            .sectionCount(4)
            .sectionNames(List.of("A", "B", "C"))
            .chapterCount(16)
            .chapterNames(List.of("1", "2", "3"))
            .build();
    log.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(completedBook));
}

public Book.BookBuilder rebuildBook(Book unfinished) {
    return Book.builder()
            .isbn(unfinished.getIsbn())
            .title(unfinished.getTitle())
            .genre(unfinished.getGenre())
            .author(unfinished.getAuthor())
            .published(unfinished.getPublished())
            .description(unfinished.getDescription())
            .sectionCount(unfinished.getSectionCount())
            .sectionNames(unfinished.getSectionNames())
            .chapterCount(unfinished.getChapterCount())
            .chapterNames(unfinished.getChapterNames());
}

POJOs are:

@ToString
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Chapters extends Sections {
    private int chapterCount;
    private List<String> chapterNames;
}

@ToString
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Sections {
    private int sectionCount;
    private List<String> sectionNames;
}

@ToString
@Data
@AllArgsConstructor
@SuperBuilder
public class Book extends Chapters {
    private String isbn;
    private String title;
    private String genre;
    private String author;
    private LocalDateTime published;
    private String description;
}


Solution 1:[1]

It happens because @SuperBuilder is defined as BookBuilder<?, ?> and java could not infer type if raw type is used Book.BookBuilder. To fix it change definition to

public Book.BookBuilder<?, ?> rebuildBook(Book unfinished) {
   ...

Also, to have correct implementation of toString and equals you need to use callSuper = true.

In addition, having @NoArgsConstructor, @AllArgsConstructor, @SuperBuilder and @Data looks a little too much. There is a way to define immutable POJO with Lombok and be able to deserialize using builder by applying @Jacksonized annotation.

@Getter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Jacksonized
@SuperBuilder
public class Chapters extends Sections {
    private int chapterCount;
    private List<String> chapterNames;
}

@Getter
@ToString
@EqualsAndHashCode
@Jacksonized
@SuperBuilder
public class Sections {
    private int sectionCount;
    private List<String> sectionNames;
}

@Getter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Jacksonized
@SuperBuilder
public class Book extends Chapters {
    private String isbn;
    private String title;
    private String genre;
    private String author;
    private LocalDateTime published;
    private String description;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alex