Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] @Superbuilder causes compile error when used from scala code due to missing type for return type. #3797

Open
kurellajunior opened this issue Dec 16, 2024 · 3 comments

Comments

@kurellajunior
Copy link

kurellajunior commented Dec 16, 2024

Describe the bug
A class (hierarchy) annotated with @SuperBuilder cannot be instantiated with the builder pattern if any of the super building methods are used from Scala code. (The class (hierarchy in question is built in a Java module before in the same maven project)

To Reproduce
In a java module:

package fun;
import lombok.experimental.SuperBuilder;

public interface Example{
    @SuperBuilder
    abstract class Parent{
        private Double mod;
    }
    @SuperBuilder
    class Child extends Parent {
        private String group;
    }
}

in the scala module

package fun
import com.rhenus.fun.coder.backend.model.sub.PrincipalDiagnosisPrediction
import fun.Example.Child

object ExampleUse {
  def main(args: Array[String]): Unit = {
    val c = Child.builder().build() // compiles fine
    val c1 = Child.builder().mod(2.4).build() // compiles, but wrong type
    val c2: Child = Child.builder().mod(2.4).build() // compile error
    val c3: Child = Child.builder().mod(2.4).build().asInstanceOf[Child] // cast to correct type compiles
  }
}

⇒ leads to compile error

Found: fun.Example#Child#ChildBuilder[?, ?]#C
Required: fun.Example.Child

Expected behavior
compiles fine

Version info (please complete the following information):

  • Lombok version : <lombok.version>1.18.34</lombok.version> (from spring boot 3.3.4)
  • Platform: IntelliJ, maven clean package

Additional context

The generated class file:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package fun;

import lombok.Generated;

public interface Example {
  public abstract static class Parent {
    private Double mod;

    @Generated
    protected Parent(final ParentBuilder<?, ?> b) {
      this.mod = b.mod;
    }

    @Generated
    public abstract static class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
      @Generated
      private Double mod;

      public ParentBuilder() {
      }

      @Generated
      public B mod(final Double mod) {
        this.mod = mod;
        return (B)this.self();
      }

      @Generated
      protected abstract B self();

      @Generated
      public abstract C build();

      @Generated
      public String toString() {
        return "Example.Parent.ParentBuilder(mod=" + this.mod + ")";
      }
    }
  }

  public static class Child extends Parent {
    private String group;

    @Generated
    protected Child(final ChildBuilder<?, ?> b) {
      super(b);
      this.group = b.group;
    }

    @Generated
    public static ChildBuilder<?, ?> builder() {
      return new ChildBuilderImpl();
    }

    @Generated
    public abstract static class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
      @Generated
      private String group;

      public ChildBuilder() {
      }

      @Generated
      public B group(final String group) {
        this.group = group;
        return (B)this.self();
      }

      @Generated
      protected abstract B self();

      @Generated
      public abstract C build();

      @Generated
      public String toString() {
        String var10000 = super.toString();
        return "Example.Child.ChildBuilder(super=" + var10000 + ", group=" + this.group + ")";
      }
    }

    @Generated
    private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
      @Generated
      private ChildBuilderImpl() {
      }

      @Generated
      protected ChildBuilderImpl self() {
        return this;
      }

      @Generated
      public Child build() {
        return new Child(this);
      }
    }
  }
}

The fix would be in the buidler() method of the Child:

    @Generated
    protected Child(final ChildBuilder<Child, ?> b) {
      super(b);
      this.group = b.group;
    }

because we do know, that we are going to return a Child here, this type would be propagated to all super builders and scala would know the correct type.

@janrieke
Copy link
Contributor

I have never used a @SuperBuilder from Scala, but this looks like a type-inference problem in the Scala compiler. If the same logic implemented as Java code compiles (which I assume), there is probably nothing Lombok can do to mitigate the problem.

@kurellajunior
Copy link
Author

As scalac is way more strict with types than javac it might still be a type problem. I will investigate further by building the generated classes by Hand in Java and see what the problem is (and how a fix could be. Maybe you can do a similar approach too?

@kurellajunior kurellajunior changed the title [BUG] @Superbuilder causes compile error when used from scala code. [BUG] @Superbuilder causes compile error when used from scala code due to missing type for return type. Dec 18, 2024
@kurellajunior
Copy link
Author

I reduced it to the maximum possible minimum I found and detected the problem. The builder()-method is too weakly typed.
Workaround til fix: cast to correct type in scala code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants