12

I am using Lombok for my project. My model looks like:

@Builder
@Data @AllArgsConstructor
public class ScreenDefinitionDTO {
    @Singular
    private List<ScreenDeclaration> screens;
}

I want to do next operation:

String screenName = ctx.screenName().getText();
ScreenDeclaration declaration = ParsingUtils
                .buildScreenDeclaration(StringUtils.trim(screenName));

Where instance is created:

public static ScreenDefinitionDTO buildEmptyScreenDTO() {
    return ScreenDefinitionDTO.builder()
            .screens(new ArrayList<>())
            .build();
}

Finally, I got:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)

When I changed creating the instance without Lombok builder pattern everything is fine:

public static ScreenDefinitionDTO buildEmptyScreenDTO() {
    return new ScreenDefinitionDTO(new ArrayList<>());
}

I couldn't understand what is wrong with Lombok's builder pattern?

catch23
  • 17,519
  • 42
  • 144
  • 217
  • I'd bet, there's more to see in the stack trace. Something like `Collection$UnmodifiableList` or alike, which sort of explains it. – maaartinus Dec 01 '17 at 04:31
  • @maaartinus I haven't seen anything related to this kind of output. – catch23 Dec 01 '17 at 15:31
  • I was wrong. I configure `lombok.singular.useGuava=true` and get `java.lang.UnsupportedOperationException at com.google.common.collect.ImmutableCollection.add(ImmutableCollection.java:221)`, but `Collections` simply relies on inheriting the throwing method. – maaartinus Dec 01 '17 at 15:44

3 Answers3

11

Due to GitHub issue

Lombok @Builder is primarily meant for immutables (and uses either Collections.unmodifiableList or Guava's ImmutableList

that's why you have UnsupportedOperationException

For greater certainty reproduce full code pattern where you have exception please.

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
yanefedor
  • 2,132
  • 1
  • 21
  • 37
  • Does any workarounds exist for it? Skipping Lombok's builder is working, however, it isn't so convenient. – catch23 Nov 30 '17 at 16:33
  • @nazar_art AFAIK no, but what about using an immutable object? Sometimes, it's a bad idea, but sometimes, it's not. There's also `toBuilder` (if you allowed it in the `@Builder` annotation), which gives you a mutable object again. – maaartinus Dec 01 '17 at 04:29
4

Try this:

@Builder
@Data @AllArgsConstructor
public class ScreenDefinitionDTO {
    @Builder.Default
    private List<ScreenDeclaration> screens = new ArrayList<>();
}

This way you are telling lombok to, on build, initialize screens with an empty ArrayList.

0

As said by @fbokovikov the @Builder annotation uses immutables so when you try to add an element in the list an exception is thrown.

dto.getScreens().add(new ScreenDeclaration()) // throws java.lang.UnsupportedOperationException

If you set a breakpoint to see the value returned by dto.getScreens() you can see its type is Collections$EmptyList. If you use the constructor of the DTO then the type is ArrayList and the exception is not thrown.

Junior Dussouillez
  • 2,327
  • 3
  • 30
  • 39