我如何获得一个构造函数,该构造函数将构造函数参数的副本分配给字段,以使实例真正不可变?下面的类具有@Value
批注,但这不是不可变的,因为它的属性不是:(问题不是特定于不可变的Collections
的,我只是用它演示了可变的DS。我正在寻找任何可变的bean代替这里的List
,例如它来自库,不在我的控制范围内]
@Value
class ImmutableBean {
List<String> props;
}
@Test
void checkImmutability() {
var props = new ArrayList<String>();
props.add("abc");
var immutableBean = new ImmutableBean(props);
System.out.println(immutableBean.getProps()); // abc
props.add("pqr");
System.out.println(immutableBean.getProps()); // abc pqr
}
对于Singleton类,您需要使用@Singular
并将您的收藏字段标记为private final
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@Value
@AllArgsConstructor
@Getter
@Builder
class ImmutableBean {
@Singular
private final List<String> props;
public static void main(String[] args) {
List<String> props = new ArrayList<>();
props.add("abc");
ImmutableBean immutableBean = ImmutableBean.builder().props(props).build();
System.out.println(immutableBean.getProps()); // abc
props.add("pqr");
System.out.println(immutableBean.getProps()); // abc
}
}
用lombook @Value注释的类使默认情况下将所有字段设为私有和final,并且不生成设置器。这意味着一旦创建对象,该对象将是不可变的,并且您不能覆盖其属性。该类使用setter方法,例如
@Test
void checkImmutability() {
var props = new ArrayList<String>();
props.add("abc");
var immutableBean = new ImmutableBean(props);
System.out.println(immutableBean.getProps()); // abc
immutableBeansetProps(new ArrayList<>()); //compile time error
}
因此lombook @Value
注释将使属性成为不可变的,因此您不能像上面的示例那样用另一个对象覆盖该对象,但是可以更新相同的对象
var props = new ArrayList<String>();
props.add("abc");
var immutableBean = new ImmutableBean(props);
System.out.println(immutableBean.getProps().hashCode()); // object1
props.add("pqr");
System.out.println(immutableBean.hashCode()); // object1 (same object)
避免这种情况的唯一方法是,您需要创建那些属性的不可变对象,例如,您可以创建不可变列表
var immutableBean = new ImmutableBean(Collections.unmodifiableList(props));
@@ Value是@Data的不可变形式; 默认情况下,所有字段都设为私有和最终字段,并且不会生成设置器。默认情况下,该类本身也被设置为final]],因为不可变性不是可以强制进入子类的东西。像@Data一样,还会生成有用的toString(),equals()和hashCode()方法,每个字段都有一个getter方法,并且还会生成一个覆盖每个参数的构造函数(除了在字段声明中初始化的最终字段之外) 。