Lombok @Builder(toBuilder = true)在子类构造函数中使用时编译错误

7

我的代码如下

package test.lombok;

import lombok.*;

@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class SuperClass {

    private int foo;

    @Getter
    public static class SubClass extends SuperClass {

        private int bar;

        @Builder(toBuilder = true)
        private SubClass(int foo, int bar) {
            super(foo);
            this.bar = bar;
        }

    }
}

如上所示,我试图在子类中使用@Builder(toBuilder = true)。
当toBuilder设置为false时,没有任何问题。
但是当我设置toBuilder = true时,我得到了一个编译错误“Error:java: foo 在test.lombok.SuperClass中具有私有访问权限”。
我想知道为什么会发生这种情况以及如何解决。

在我看来,这是Lombok的一个bug。如果我没错的话,请您提交一个问题吗?(https://github.com/rzwitserloot/lombok/issues) - maaartinus
1
没问题。我已经发布了一个问题#1488 - Juan Zhong
4个回答

4

Lombok 在注解 @Builder 中设置属性 toBuildertrue 后,会尝试在 SubClass 中创建 toBuilder 方法。该方法返回 SubClassBuilder 类。以下是 toBuilder 方法的样式:

public SuperClass.SubClass.SubClassBuilder toBuilder() {
    return (new SuperClass.SubClass.SubClassBuilder())
                    .foo(this.foo).bar(this.bar);
}

你可能已经注意到,toBuilder 方法直接访问 foo 属性而不是通过 getFoo 方法。由于 foo 是私有的并且属于父类 SuperClass,因此会出现以下错误:

Error:java: foo 在 test.lombok.SuperClass 中具有私有访问权限


3
问题出在SubClass中的toBuilder方法的实现方式上:
public SuperClass.SubClass.SubClassBuilder toBuilder() {
    return (new SuperClass.SubClass.SubClassBuilder()).foo(this.foo).bar(this.bar);
}

与其使用 this.foo,应该使用 super.foo,这样代码就能编译。在这种情况下,访问 super.foo 是可能的,因为 SubClassSuperClass 的内部类;否则,Java 也将不允许使用 super.foo

如果你想看到 lombok 生成的代码,请将 foo 声明为 public,然后编译,然后进行 delombok(或反编译),你就会看到像这样的代码(然后将属性更改为 private,以查看错误发生的位置):

import java.beans.ConstructorProperties;

public class SuperClass {
    public int foo;

    @ConstructorProperties({"foo"})
    protected SuperClass(int foo) {
        this.foo = foo;
    }

    public int getFoo() {
        return this.foo;
    }

    public static class SubClass extends SuperClass {
        private int bar;

        private SubClass(int foo, int bar) {
            super(foo);
            this.bar = bar;
        }

        public static SuperClass.SubClass.SubClassBuilder builder() {
            return new SuperClass.SubClass.SubClassBuilder();
        }

        public SuperClass.SubClass.SubClassBuilder toBuilder() {
            return (new SuperClass.SubClass.SubClassBuilder()).foo(this.foo).bar(this.bar);
        }

        public int getBar() {
            return this.bar;
        }

        public static class SubClassBuilder {
            private int foo;
            private int bar;

            SubClassBuilder() {
            }

            public SuperClass.SubClass.SubClassBuilder foo(int foo) {
                this.foo = foo;
                return this;
            }

            public SuperClass.SubClass.SubClassBuilder bar(int bar) {
                this.bar = bar;
                return this;
            }

            public SuperClass.SubClass build() {
                return new SuperClass.SubClass(this.foo, this.bar);
            }

            public String toString() {
                return "SuperClass.SubClass.SubClassBuilder(foo=" + this.foo + ", bar=" + this.bar + ")";
            }
        }
    }
}

编辑:感谢 @maaartinus 提供了 super.foo 的提示,答案已更新。


谢谢,MondKin。非常详细的解释!所以我猜没有办法让lombok生成SubClass的toBuilder()?在toBuilder()中是否可以调用getFoo()而不是this.foo? - Juan Zhong
一种解决方案是将 foo 声明为受保护的,这样您就可以从 SubClass 内部访问它。否则,我不知道有什么方法告诉 lombok 使用访问器方法。 - Daniel
2
@MondKin 我猜你错过了一个更好的解决方案:使用在同一源文件中声明的super.foo作为字段是可访问的。显然,这应该在Lombok中得到修复。 - maaartinus
@maaartinus 您是正确的,我没有注意到在 lombok 方面用 super 替换 this 将解决问题。 - Daniel

1
据我所知,这是Lombok的一个bug。有三种方法可以访问foo,但只有一种方法有效:
  • 简单的foo会导致“无法对非静态字段foo进行静态引用”
  • Lombok使用的this.foo会导致“SuperClass.foo字段不可见”
  • super.foo有效!
据我所知,同一源文件中声明的所有内容都可以某种方式被访问,但找到正确的表达式可能有些棘手。

1

从Lombok 1.18.12开始,您可以使用新的实验性功能@SuperBuilder

它支持toBuilder

import lombok.*;
import lombok.experimental.SuperBuilder;

@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@ToString // For demonstration purposes only; only used in the main method.
@SuperBuilder(toBuilder = true)
public class SuperClass {

    private int foo;

    @Getter
    @ToString(callSuper = true) // For demonstration purposes only; only used in the main method.
    @SuperBuilder(toBuilder = true)
    public static class SubClass extends SuperClass {

        private int bar;

        private SubClass(int foo, int bar) {
            super(foo);
            this.bar = bar;
        }
    }

    public static void main(String[] argv) {
        SubClass sc = SubClass.builder()
                .foo(1)
                .bar(2)
                .build();
        System.out.println(sc);
    }
}

输出:

SuperClass.SubClass(super=SuperClass(foo=1), bar=2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接