使用Immutables实现继承无默认构造方法类的自定义抽象不可变类
解决Immutables框架继承无默认构造父类的问题
场景与问题
需要基于Immutables框架创建一个继承自无默认构造方法父类的抽象不可变类(以Spring的ApplicationEvent为例),同时要支持Builder功能。
原尝试的代码如下:
@Value.Immutable @Value.Style( privateNoargConstructor = true, get = {"is*", "get*"}, init = "set*", passAnnotations = Builder.class) public abstract class CustomEvent extends ApplicationEvent { // 尝试添加构造但未达到预期 abstract String getFoo(); }
父类ApplicationEvent的核心定义:
public abstract class ApplicationEvent extends EventObject { public ApplicationEvent(Object source) { super(source); } }
当在抽象类中手动添加匹配父类的构造后:
private CustomEvent(Object source) { super(source); }
生成的ImmutableCustomEvent构造器并未正确调用父类构造,而是生成了无参构造且未处理父类依赖:
private ImmutableCustomEvent() { this.foo = null; }
期望生成的构造器应为以下两种之一:
// 方式1:无参构造调用父类兼容逻辑(传入null) private ImmutableCustomEvent() { super(null); this.foo = null; }
// 方式2:带source参数的构造,传递给父类 private ImmutableCustomEvent(Object source) { super(source); this.foo = null; }
解决方案
要让Immutables正确处理父类的构造依赖,需要通过框架注解标记参数,并规范抽象类的构造定义,具体有两种可行方式:
方式1:使用@Value.Parameter标记父类构造参数
@Value.Immutable @Value.Style( get = {"is*", "get*"}, init = "set*", passAnnotations = Builder.class ) public abstract class CustomEvent extends ApplicationEvent { // 用@Value.Parameter标记父类构造所需的source参数 @Value.Parameter abstract Object getSource(); abstract String getFoo(); // 抽象类构造必须调用父类构造,参数通过框架生成的sourceOf方法获取 protected CustomEvent() { super(ImmutableCustomEvent.sourceOf((ImmutableCustomEvent) this)); } }
方式2:使用@Value.Constructor显式绑定构造
@Value.Immutable @Value.Style( get = {"is*", "get*"}, init = "set*", passAnnotations = Builder.class ) public abstract class CustomEvent extends ApplicationEvent { abstract String getFoo(); // 用@Value.Constructor标记构造,绑定父类的source参数 @Value.Constructor protected CustomEvent(Object source) { super(source); } // 定义对应source参数的抽象方法 abstract Object getSource(); }
补充:支持无参构造与默认值
如果需要保留无参构造的生成(即允许source为null),可以给getSource()添加默认实现,并调整@Value.Style配置:
@Value.Immutable @Value.Style( privateNoargConstructor = true, get = {"is*", "get*"}, init = "set*", passAnnotations = Builder.class ) public abstract class CustomEvent extends ApplicationEvent { @Value.Parameter abstract Object getSource(); // 给source设置默认null,无参构造时会传入父类 default Object getSource() { return null; } abstract String getFoo(); protected CustomEvent() { super(ImmutableCustomEvent.sourceOf((ImmutableCustomEvent) this)); } }
关键说明
- 不要手动编写private构造,Immutables需要通过注解(
@Value.Parameter/@Value.Constructor)识别父类构造的依赖参数 - 抽象类的构造必须调用父类构造,参数需通过框架生成的工具方法(如
sourceOf)或抽象方法获取,确保生成的不可变类能正确传递参数 - 若启用
privateNoargConstructor = true,必须保证父类构造能接受null参数,或通过默认方法给参数设置默认值
内容的提问来源于stack exchange,提问作者Pwnstar
相关产品推荐
相关产品推荐

