使用Lombok SuperBuilder在继承类中为Function类型字段设置含其他字段引用的默认值时出现静态上下文错误的求助
这个问题我太熟悉了!你猜的没错,根源就是Lombok生成的@SuperBuilder是静态Builder类,而@Builder.Default的默认值是在这个静态Builder的上下文里初始化的——这时候还根本没有SomeClass的实例,lambda里引用的fieldA是实例级别的非静态字段,自然会抛出non-static field cannot be referenced from a static context的错误。
下面给你几个可行的解决方案,你可以根据自己的需求选:
方案1:自定义构造函数处理默认值
放弃@Builder.Default,转而在自定义的构造函数里初始化fieldB。因为构造函数执行时,fieldA已经被赋值完成,lambda可以安全捕获当前实例的fieldA:
@SuperBuilder(toBuilder = true) class SomeClass extends Parent { private final Xxxx fieldA; private final Function<InputType, OutputType> fieldB; // 覆盖Lombok生成的构造函数 protected SomeClass(SomeClassBuilder<?, ?> builder) { super(builder); this.fieldA = builder.fieldA; // 如果Builder没有传入fieldB,就用引用fieldA的lambda作为默认值 this.fieldB = builder.fieldB != null ? builder.fieldB : input -> this.fieldA.xxxx(); // 这里的this是已经初始化的SomeClass实例 } }
这样既保留了@SuperBuilder的所有功能,又解决了静态上下文的问题。注意把fieldA和fieldB声明为final,保证不可变性,也让lambda捕获更安全。
方案2:把fieldB改成实例方法(最简洁)
如果你的fieldB不需要支持动态替换(也就是不需要在Builder里传入自定义的Function),直接把它改成实例方法就好,完全绕开Builder的问题:
@SuperBuilder(toBuilder = true) class SomeClass extends Parent { private final Xxxx fieldA; // 原来的Function逻辑直接写成实例方法 public OutputType processFieldB(InputType input) { return fieldA.xxxx(); } }
这种方式代码更简洁,也符合面向对象的设计,毕竟依赖实例字段的逻辑本来就该是实例方法的职责。
方案3:自定义Builder类控制初始化
如果你需要更灵活的Builder逻辑,可以自定义Builder类,在build()方法里处理fieldB的默认值:
@SuperBuilder(toBuilder = true, builderMethodName = "internalBuilder") class SomeClass extends Parent { private final Xxxx fieldA; private Function<InputType, OutputType> fieldB; // 这里不能是final,因为要在build后赋值 // 对外暴露的builder方法,替代Lombok生成的默认方法 public static SomeClassBuilder<?, ?> builder() { return internalBuilder(); } // 自定义Builder类,继承父类的Builder public static class SomeClassBuilder<C extends SomeClass, B extends SomeClassBuilder<C, B>> extends Parent.ParentBuilder<C, B> { @Override public C build() { // 先调用父类build创建实例 C instance = super.build(); // 如果fieldB没设置,就用实例的fieldA创建lambda if (this.fieldB == null) { instance.fieldB = input -> instance.fieldA.xxxx(); } return instance; } } }
不过这个方案需要把fieldB改为非final,不如方案1直接,所以只推荐在有复杂Builder需求时使用。
总结一下:优先选方案1(需要保留fieldB可配置)或者方案2(不需要动态替换逻辑),这两个方案最省心也最符合Lombok的设计初衷。
内容的提问来源于stack exchange,提问作者cherrythefatbunny

