You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 21:21:48