11

In my project, lombok is used to avoid writing getters and setters for a class. I have two classes Child extends Parent:

@Value
@Builder
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Parent {
    @Nonnull
    @JsonProperty("personId")
    private final String personId;

    @JsonProperty("personTag")
    private final String personTag;
    ...
}

And

@Value
@Builder
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Child extends Parent {
    @Nonnull
    @JsonProperty("childId")
    private final String childId;
    ...
}

But this doesn't seems work as no default constructor available in Parent. I'm not familiar with the lombok annotation. Is there any good way to extend the Base class and use the lombok annotation at the same time?

Kevin
  • 111
  • 1
  • 1
  • 3
  • Use `@NoArgsConstructor` for Default constructor – Phenomenal One Sep 15 '18 at 06:17
  • Maybe you add the errors you get to your question? Are you trying to made classes immutable on purpose? – pirho Sep 15 '18 at 07:50
  • Hi @pirho The two errors are thrown: "There is no default constructor in available in Parent" & "Cannot inherit from final Parent". I think rzwitserloot's comment below answers part of my question. I'm also not sure about a proper way to deal with my use case - extending the Parent model or creating a new model. – Kevin Sep 15 '18 at 23:49
  • Hey @MaruthiAdithya You're right, that seems to be a viable solution for the "no default constructor" error. – Kevin Sep 15 '18 at 23:51
  • In Java, you cannot enforce immutability on subclasses. Thus, lombok makes the class `final` when using `@Value` to make sure no one creates a (possibly mutable) subclass. If you are sure no one will do this in your case, you can still make the superclass immutable without `@Value`: Omit the setters, and make fields `final`. If creating instances using `@SuperBuilder` is not sufficient (see answer of @rzwitserloot), you have to create a manual constructor with all fields as parameters in `Child` that calls the `@AllArgsConstructor` of `Parent`. – Jan Rieke Sep 16 '18 at 10:36

2 Answers2

6

TL;DR: add @NonFinal annotation to your superclass

Details: @Value annotation makes the class final, so you cannot inherit from it. Experimental @NonFinal annotation should prevent this.

import lombok.Value;
import lombok.experimental.NonFinal;

@Value
@NonFinal
public class Parent {

REF: https://projectlombok.org/features/Value


NOTE: For performance reasons (if it matters) final (value) objects can be (theoretically) super fast. The optimizer can allocate them in the stack memory, or reuse the same stack block in cycles, so no GC overheads.

(This is similar to how .NET structure value objects are usually allocated by .NET framework)

By adding @NonFinal such an optimization opportunity disappears.

epox
  • 9,236
  • 1
  • 55
  • 38
5

Class hierarchies + lombok doesn't work particularly well, in the sense that the lombok operations done on your Child class don't know anything about parent.

However, your specific question seems answerable:

The Parent class has a constructor that takes all fields, because you asked lombok to make this constructor via @AllArgsConstructor. Therefore, it does not have a no-args constructor. If you want both constructors (the one that takes all fields + a second one that takes no arguments, a.k.a. the default constructor), also add a @NoArgsConstructor annotation to tell lombok that you want that.

NB: @Builder does not work with hierarchy either, but the fresh new @SuperBuilder feature does. I'm pretty sure you want to replace @Builder with @SuperBuilder here. SuperBuilder requires that ALL classes in the hierarchy are annotated with @SuperBuilder and not @Builder.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Yeah, adding another "@NoArgsConstructor" annotation fixes the "no default constructor" issue. And a possible workaround seems to be like: replace "@Value" with "@Data" and remove "final" of the fields in the Parent model. But that will make the Parent mutable. The base model (Parent) is already in use. I'm not sure if it's good to extend it. Or maybe creating a new version of the model that covers use cases for Child is a better solution? – Kevin Sep 15 '18 at 23:43