Lombok @SuperBuilder inheritance with generics always shows "capture of ?"

44 views Asked by At

I am trying to create a number of classes with some inheritance hierarchy. To instantiate these classes I want to use builders, but I also want to override some methods in these builders. Since there is inheritance, I implemented it with @SuperBuilder. But throwing generics in, has messed up a bit with everything and I am unsure how to fix it properly.

I have a base class defined as:

@SuperBuilder
public class Base<T> {
    protected T payload;
    protected String name;
    protected String attribute;
}

now I wanted to create a new class that extends this one but would bound the type of the payload attribute, as well as set some default values, so I defined it as:

@SuperBuilder
public class ChildA<T extends Payload> extends Base<T> {


    public abstract static class ChildABuilder
            <T extends Payload, C extends ChildA<T>, B extends ChildA.ChildABuilder<T, C, B>>
            extends Base.BaseBuilder<T, C, B> {

        ChildABuilder() {
            super.name("CHILD A");
        }

        @Override
        public B payload(T payload) {
            return super.payload(payload).attribute(payload.attribute());
        }
    }
}

but this would throw an error saying "both methods have same erasure, yet neither hides the other". So I changed the parent @SuperBuilder to @SuperBuilder(builderMethodName = "baseBuilder"), which solved it. (Let's assume Payload also has an 'attribute' attribute, just for the sake of illustrating my problem).

However, I am unable to actually build an instance of this. Trying to do the following:

ChildA<PayloadChild> childA = ChildA.<PayloadChild>builder()
                .payload(new PayloadChild())
                .build();

results in an issue in the .payload(new PayloadChild()), stating that T was expected but PayloadChild was provided. (PayloadChild is assumed to extend Payload).

I have tried 'delomboking' the code, and made a somewhat functional class. But I still have issues building instances of the Base class, for example for testing, and I don't believe delomboking should be the solution.

What am I doing wrong? What changes would be required for this to work?

EDIT 1: After @Sweeper 's comment, I realised my code does work. It is the IntellIJ IDE (or the Lombok plugin for that matter) that's raising the errors, but it does actually compile and run. I'll keep the question open for now, in case anyone has some workaround or something to make the IDE not complain.

1

There are 1 answers

0
Jan Rieke On

The first problem you encountered ("...have the same erasure, yet neither hides the other") is due to limitations of the Java language. Static methods cannot be overridden, but can only hide each other. In this case the builder() method in the ChildA subclass does not hide the one from the Base superclass, because they have a different signature. However, the compiler cannot determine statically which method you want to call (because "they have the same erasure", i.e. after removing type parameters they look exactly the same).

You already found a solution to that: Rename one of the builder() methods. That is a valid solution. However, from what your code looks like, it seems it is not intended to use the Base class directly. If this is the case, it may be better to

  • remove the builder() method in the Base class by using @SuperBuilder(builderMethodName = ""), or
  • to make the Base class abstract.

In this way, you won't have an additional static builder() method that could confuse developers if they see it in auto-completion.

The second problem you describe in fact only occurs in IntelliJ, but not in Eclipse or when compiling with javac. Consider filing a bug.