繁体   English   中英

创建具有可变字段的不可变记录

[英]Creating immutable Records having mutable fields

我想创建一个不可变的记录,它有 2 个可变字段 Date 和一个 HashMap

public record ImmutableRecord(String name, LocalDate admissionDate, Date dateOfBirth, Map<String, Integer> metaData) {

    public ImmutableRecord{
        // Date is a mutable field
        dateOfBirth = new Date(dateOfBirth.getTime());

        // HashMap is a mutable field
        Map<String, Integer> tempMap = new HashMap<>();
        for(Map.Entry<String, Integer> entry: metaData.entrySet()){
            tempMap.put(entry.getKey(), entry.getValue());
        }
        metaData = tempMap;

        //Can I use following instead of above for deep copying the map?
        metaData = Map.copyOf(metaData);
    }

}

使用 forEach 深度克隆每个字段或使用 Map.copyOf 以下哪种方法是正确的

首先,在设计上,一般来说复制数据的责任应该落在实例化记录的调用方法上。 构造函数通常应该进行这种复制。

让我们更改此记录的名称,因为“ImmutableRecord”是多余的,并且无法反映您的问题域。 鉴于录取日期字段,显然您打算代表学生。 所以你的整个 class 定义可能是:

public record Student ( String name, LocalDate admitted, LocalDate dateOfBirth, Map<String, Integer> metaData ) {}

The modern and immutable LocalDate class should always be used instead of the mutable legacy class java.sql.Date class.

调用方法应该进行复制,如下所示。

关于您尝试克隆 map:

    Map<String, Integer> tempMap = new HashMap<>();
    for(Map.Entry<String, Integer> entry: metaData.entrySet()){
        tempMap.put(entry.getKey(), entry.getValue());
    }

这种复制不是深度克隆。 您没有复制密钥的内容 您也没有复制值的内容。 您确实创建了另一个Map object。 但是旧的 map 和新的 map 都有包含对相同键和值对象的引用的条目。

您的复制实际上与仅将旧 map 传递给新 map 的构造函数相同。 新旧 map 都有指向相同键对象和相同值对象的条目。

Map < String , Integer > metaData = new HashMap <> ( oldMetaData ) ;

Your copying is nearly the same as Map.copyOf( oldMap ) except that copyOf produces an unmodifiable map of some unspecified class implementing Map rather than a modifiable HashMap . 但就像您上面的代码一样,旧映射和新映射都指向相同的键对象和相同的值对象。

所以调用代码看起来像这样:

Student someStudent = new Student( "Alice" , … ) ;
…
Student twinStudent = 
    new Student( 
        "Bob" , 
        LocalDate.of( 2021, Month.MARCH, 23 ), 
        someStudent.dateOfBirth, 
        Map.copyOf( someStudent.metaData ) 
    ) 
;

记录功能的目的是简明地定义一个 class,其主要目的是透明和不可变地传递数据。 所以理想情况下,记录应该包含不可变的内容。 这是另一个更喜欢Map.copyOf而不是新的HashMap的原因。

正如所评论的,另一个更喜欢Map.copyOf的原因是,如果传递的 map 已经不可修改,则直接返回传递的 map。 使用较少的 memory,使用较少的 CPU。

正如所评论的,使用Map.copyOf的一个可能限制是,在 map 参考中,任何键或任何值中都不允许出现空值。 Map.copyOf是一个“无空区”,这是我自己新创造的术语。

如果您的方案中可以容忍空值,则替代方案是Collections.unmodifiableMap 并且,对于防御性编程,复制 map 被Collections.unmodifiableMap包裹。

metaData = Collections.unmodifiableMap( new HashMap<>( oldMetaData ) ) ;

最好将设计决策视为二元的......即,它是不可变的吗?

在你的情况下,你有一些可变的东西。 在这种情况下,只需使用 Lombok @Data来构建它。

此外,您还可以:

  • 添加@Builder,为你提供一个流利的builder
  • 添加 @With 为您提供一个复制修改器

暂无
暂无

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

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