我们如何通过可变引用来维护类的不变性 [英] How can we maintain Immutability of a class with a mutable reference

查看:91
本文介绍了我们如何通过可变引用来维护类的不变性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道使我们的类不可变的所有基本规则但是当有另一个类引用时我有点困惑。我知道如果有集合而不是地址那么我们可以使用 Collections.unmodifiableList(new ArrayList<>(modifiable)); 然后我们可以使我们的类不可变。但在下面的情况下,我仍然无法得到这个概念。

I know all the basic rules to make our class immutable but I am a little confused when there is another class reference. I know if there is collection instead of Address then we can make use of Collections.unmodifiableList(new ArrayList<>(modifiable)); and then we can make our class immutable. But in below case I am still unable to get the concept.

public final class Employee{
    private final int id;
    private Address address;
    public Employee(int id, Address address)
    {
        this.id = id;
        this.address=address;
    }
    public int getId(){
        return id;
    }
    public Address getAddress(){
        return address;
    }
}

public class Address{
    private String street;
    public String getStreet(){
        return street;
    }
    public void setStreet(String street){
        this.street = street;
    }
}


推荐答案

好,这个概念是阅读JLS并理解它。在这种情况下,JLS说:

Well, the concept is reading the JLS and understanding it. In this case, the JLS says:


最终字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。线程安全的不可变对象被所有线程视为不可变对象,即使使用数据争用传递线程之间的不可变对象的引用也是如此。这可以提供安全保证,防止错误或恶意代码滥用不可变类。必须正确使用final字段以保证不变性。

final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. final fields must be used correctly to provide a guarantee of immutability.

最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段;并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用。如果遵循此操作,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还会看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的。

The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.

所以你需要:


  1. 制作地址最终和私人。

  2. 对于任何可变对象,您必须阻止从外部看到对该对象的引用。

  1. Make address both final and private.
  2. For any mutable object, you must prevent the reference to that object from being seen externally.

在这种情况下,#2可能意味着您不能像使用 getAddress()那样返回对地址的引用。 你必须在构造函数中制作一个防御性副本。即,制作任何可变参数的副本,并将副本存储在Employee中。如果您无法制作防御性副本,那么就无法使Employee成为不可变的。

In this case, #2 probably means you can't return a reference to Address like you have with getAddress(). And you have to make a defensive copy in the constructor. I.e., make a copy of any mutable parameter, and store the copy in Employee. If you can't make a defensive copy, there's really no way to make Employee immutable.

public final class Employee{
    private final int id;
    private final Address address;
    public Employee(int id, Address address)
    {
        this.id = id;
        this.address=new Address();  // defensive copy
        this.address.setStreet( address.getStreet() );
    }
    pulbic int getId(){
        return id;
    }
    public Address getAddress() {
        Address nuAdd = new Address(); // must copy here too
        nuAdd.setStreet( address.getStreet() );
        return nuAdd;
}

实施 clone()或类似的东西(复制ctor)会使复杂的类更容易创建防御对象。但是,我认为最好的建议是使地址不可变。一旦你这样做,你就可以自由地传递它的引用,而不会出现任何线程安全问题。

Implementing clone() or something similar (a copy ctor) would make creating defensive objects easier for complicated classes. However, the best recommendation I think would be to make Address immutable. Once you do that you can freely pass around its reference without any thread-safety issues.

在这个例子中,注意我做 NOT 必须复制 street 的值。 Street 是一个字符串,字符串是不可变的。如果 street 由可变字段(例如整数街道号码)组成,那么我 必须复制 street 也等等无限广告。这就是为什么不可变对象如此有价值,它们打破了无限复制链。

In this example, notice I do NOT have to copy the value of street. Street is a String, and strings are immutable. If street consisted of mutable fields (integer street number for example) then I would have to make a copy of street also, and so on ad infinitum. This is why immutable objects are so valuable, they break the "infinite copy" chain.

这篇关于我们如何通过可变引用来维护类的不变性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆