199

I have a class

@Value
@NonFinal
public class A {
    int x;
    int y;
}

I have another class B

@Value
public class B extends A {
    int z;
}

lombok is throwing error saying it cant find A() constructor, explicitly call it what i want lombok to do is to give annotation to class b such that it generates the following code:

public class B extends A {
    int z;
    public B( int x, int y, int z) {
        super( x , y );
        this.z = z;
    }
}

Do we have an annotation to do that in Lombok?

Jasper
  • 11,590
  • 6
  • 38
  • 55
Sandeep Nagaraj
  • 2,122
  • 2
  • 12
  • 10

6 Answers6

298

This is not possible in Lombok. Although it would be a really nice feature, it requires resolution to find the constructors of the super class. The super class is only known by name the moment Lombok gets invoked. Using the import statements and the classpath to find the actual class is not trivial. And during compilation you cannot just use reflection to get a list of constructors.

It is not entirely impossible but the results using resolution in val and @ExtensionMethod have taught us that is it hard and error-prone.

Disclosure: I am a Lombok developer.

Roel Spilker
  • 32,258
  • 10
  • 68
  • 58
  • 1
    @roel-spilker We understand the complexities behind it. But can Lombok provide an `inConstructor` method for constructor annotations where we can specify which constructor of `super` shall Lombok inject in the generated constructor? – Manu Manjunath Feb 16 '16 at 05:38
  • 2
    afterConstructor would be nice as well to do some automatic initialization – Pawel Mar 02 '16 at 15:16
  • @Manu/@Pawel: see lombok enhancement request: https://github.com/peichhorn/lombok-pg/issues/78 (currently open) – JJ Zabkar Apr 13 '16 at 16:33
  • Since @Builder is in official release see: https://github.com/rzwitserloot/lombok/issues/853 – Sebastian Oct 28 '16 at 21:03
  • 1
    @Roel Is it possible to make something like a `@CopyConstructor`? This way we can annotate the `class Base` with it and in the `class Der extends Base` the following will be short and easy to be done manually: `public Der(Base base, Object extraProp) { super(base); this.extraProp = extraProp; }` – egelev Feb 12 '20 at 07:39
  • Hi Roel Spilker, Can we try to generate the child class' constructor from the other side? I mean that the @RequiredArgsConstructor may have a switch to generate ctors in the child classes and delegate them all to the parent with the annotation maybe or something similar? – Lóránt Miglécz Feb 26 '20 at 09:09
  • Late thought: It would probably be possible to manually pass delegate arguments to `@Value` in a `parentArgs` parameter or similar? Probably would still look cleaner than writing the constructor by hand in an environment that otherwise uses Lombok. – Egor Hans Mar 21 '22 at 07:36
31

Version 1.18 of Lombok introduced the @SuperBuilder annotation. We can use this to solve our problem in a simpler way.

You can refer to https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3.

so in your child class, you will need these annotations:

@Data
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)

in your parent class:

@Data
@SuperBuilder
@NoArgsConstructor
Alan Ho
  • 646
  • 9
  • 5
  • 3
    With SuperBuilder, does AllArgsConstructor then include parameters from both parent and child? Thanks! – 0xTomato Jan 11 '21 at 03:53
  • We cannot solve this in case when SuperBuilder is not needed in result class. Especially when this builder is far away from ideal. In my case my builders always doing deep copy, lombok's - no. – m1ld Aug 21 '21 at 18:22
  • java: Generating equals/hashCode with a supercall to java.lang.Object is pointless. – Sanjeev Sep 23 '21 at 18:28
27

Lombok Issue #78 references this page https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ with this lovely explanation:

@AllArgsConstructor 
public class Parent {   
     private String a; 
}

public class Child extends Parent {
  private String b;

  @Builder
  public Child(String a, String b){
    super(a);
    this.b = b;   
  } 
} 

As a result you can then use the generated builder like this:

Child.builder().a("testA").b("testB").build(); 

The official documentation explains this, but it doesn’t explicitly point out that you can facilitate it in this way.

I also found this works nicely with Spring Data JPA.

JJ Zabkar
  • 3,792
  • 7
  • 45
  • 65
  • Can you provide an example of this being used with Spring Data JPA? – Marc Zampetti Jun 02 '16 at 18:53
  • 73
    This does not answer the question at all. Instead, it does the work by hand, while the question was how to generate it. At the same time, it makes the whole thing more confusing by dragging @Builder, which does not have anything to do with the question. – Jasper Aug 04 '16 at 13:49
  • 13
    Actually this is very useful for those who just want to create an inheritance structure and then use builder. This is 99% reason I use #lombok anyways. Sometimes we just have to handcraft stuffs to make it work the way we desire.So thanks @jj-zabkar – Babajide Prince Mar 02 '18 at 10:17
  • but then; simply code the self+parent arg constructor. No need to use a builder – Juh_ Aug 02 '19 at 14:02
  • It'll work in STS & eclipse, but when you generate JAR file of your application, most probably it will fail. I tried Both SuperBuilder, Builder for inheritence. Both got failed. Be careful !! – Satish Patro Nov 01 '19 at 11:45
  • What if there are 3 classes such thast Class C extends Class B and Class B extends Class A? – firstpostcommenter May 07 '20 at 09:43
12

Lombok does not support that also indicated by making any @Value annotated class final (as you know by using @NonFinal).

The only workaround I found is to declare all members final yourself and use the @Data annotation instead. Those subclasses need to be annotated by @EqualsAndHashCode and need an explicit all args constructor as Lombok doesn't know how to create one using the all args one of the super class:

@Data
public class A {
    private final int x;
    private final int y;
}

@Data
@EqualsAndHashCode(callSuper = true)
public class B extends A {
    private final int z;

    public B(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }
}

Especially the constructors of the subclasses make the solution a little untidy for superclasses with many members, sorry.

Arne Burmeister
  • 20,046
  • 8
  • 53
  • 94
  • 2
    Could you explain a bit more why "subclasses need to be annotated by `@EqualsAndHashCode`"? Isn't this annotation included by `@Data`? Thx :) – Gerard Bosch Jul 11 '17 at 08:42
  • 3
    @GerardB `@Data` also creates equals() and hashCode() but don't cares about any inheritance. To make sure the superclass equals() and hashCode() is used, you need the explicit generation with callSuper – Arne Burmeister Jul 19 '17 at 07:00
  • I thought class B could be @Value. Can it? So JVM would be able to optimize its performance by storing on Stack. – epox Aug 11 '23 at 22:39
7

for superclasses with many members I would suggest you to use @Delegate

@Data
public class A {
    @Delegate public class AInner{
        private final int x;
        private final int y;
    }
}

@Data
@EqualsAndHashCode(callSuper = true)
public class B extends A {
    private final int z;

    public B(A.AInner a, int z) {
        super(a);
        this.z = z;
    }
}
Kris
  • 71
  • 1
  • 2
3

If child class has more members, than parent, it could be done not very clean, but short way:

@Data
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class User extends BaseEntity {
    private @NonNull String fullName;
    private @NonNull String email;
    ... 

    public User(Integer id, String fullName, String email, ....) {
        this(fullName, email, ....);
        this.id = id;
    }
}

@Data
@AllArgsConstructor
abstract public class BaseEntity {
   protected Integer id;

   public boolean isNew() {
      return id == null;
   }
}
Grigory Kislin
  • 16,647
  • 10
  • 125
  • 197
  • Instance variables should not be made protected lightheartedly, as it most probably violates object orientation principles. – Lars Gendner Oct 22 '21 at 16:26