繁体   English   中英

如何为不可变类创建默认构造函数

[英]How to create default constructor for immutable class

我喜欢根据本文使我的对象不可变(为什么对象必须不可变)

但是,我正在尝试使用Jackson Object Mapper解析对象。 我最初得到的是JsonMappingException: No suitable constructor found for type [simple type, class ]: cannot instantiate from JSON object.

如前所述我可以修复它在这里 ,通过提供一个默认的构造,使我场的非决赛。

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@AllArgsConstructor
// @NoArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Data
public class School {

    @NonNull
    private final String schoolId;

    @NonNull
    private final String schoolName;
}

为了克服这个问题,我应该遵循什么良好的编程风格? 唯一的解决方法是使我的对象可变吗?

我可以使用不使用默认构造函数的其他映射器吗?

您可以使用Jackson工厂(用@JsonCreator注释的方法)从地图上读取字段并调用您的非默认构造函数:

class School {
    //fields

    public School(String id, String name) {
        this.schoolId = id;
        this.schoolName = name;
    }

    @JsonCreator
    public static School create(Map<String, Object> object) {
        return new School((String) object.get("schoolId"), 
                          (String) object.get("schoolName"));
    }

    //getters
}

杰克逊将使用JSON的Map版本调用create方法。 这有效地解决了问题。

我相信您的问题是寻找Jackson解决方案,而不是寻求新的模式/风格。

TL; DR:使用lombok并避免使用默认构造函数

  • 使用@Value制作不可变的数据类
  • @JsonProperty("name-of-property")注释所有字段
  • lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty添加到您的lombok.config ,将其复制到生成的构造函数中
  • 创建一个用@JsonCreator注释的全参数构造@JsonCreator

例:

@Value
@AllArgsConstructor(onConstructor_ = @JsonCreator)
class School {
    @JsonProperty("schoolId")
    String schoolId;
    @JsonProperty("schoolName")
    String schoolName;
}

长答案

使用@JsonCreator注释的静态工厂方法有一个imo更好的替代方法,该方法为所有Elements具有构造函数(无论如何,这是不可变类的要求)。 注释 @JsonCreator也注释了所有参数@JsonProperty是这样的:

class School {
    //fields

    @JsonCreator
    public School(
            @JsonProperty("id") String id,
            @JsonProperty("name") String name) {
        this.schoolId = id;
        this.schoolName = name;
    }

    //getters
}

这些是@JsonCreator批注为您提供的选项。 它在其文档中像这样描述它们:

  • 没有参数的JsonProperty批注的单参数构造函数/工厂方法:如果是这样,则这就是所谓的“委托创建者”,在这种情况下,Jackson首先将JSON绑定到参数的类型,然后调用creator。 这通常与JsonValue(用于序列化)结合使用。
  • 构造函数/工厂方法,其中每个参数都用JsonProperty或JacksonInject注释,以指示要绑定的属性的名称

在某些情况下,甚至可能不需要显式指定参数名称。 有关@JsonCreator的文档进一步指出:

还要注意,除非您使用可以检测参数名称的扩展模块之一,否则所有JsonProperty批注都必须指定实际名称(“ default”不为空字符串)。 这是因为8之前的默认JDK版本无法存储和/或从字节码中检索参数名称。 但是对于JDK 8(或使用诸如Paranamer之类的帮助程序库,或诸如Scala或Kotlin之类的其他JVM语言),指定名称是可选的。

另外这也将与龙目岛版本的工作很好1.18.3或向上,在这里你可以添加lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonPropertylombok.config ,因此有它的复制JsonProperty注释构造,假设您确实用它注释了所有字段(无论如何,imo都应该这样做)。 要将@JsonCreator -annotation放在构造函数上,可以使用实验性onX功能 对不可变数据类使用@Value@Value ,您的DTO可能看起来像这样(未经测试):

@Value
//@AllArgsConstructor(onConstructor = @__(@JsonCreator)) // JDK7 or below
@AllArgsConstructor(onConstructor_ = @JsonCreator) // starting from JDK8
class School {
    @JsonProperty("schoolId")
    String schoolId;
    @JsonProperty("schoolName")
    String schoolName;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM