返回私有数据的副本而不是引用

And*_*bey 11 java reference

在令人惊讶的书"Java the Good Parts"中,作者给出了一些代码,这些代码在其getter方法中返回一个对象的副本(与一个封装良好的字段一起使用),然后说明:

这种方法试图返回私有数据的副本而不是对私有数据的引用,这通常是一个好主意

为什么会这样?我认为封装的目标是确保没有人能够真正改变私人成员.那我为什么要写这样的东西呢

private someType fieldName = new someType(); 

...
Run Code Online (Sandbox Code Playgroud)

同时定义它的getter(假设有某种复制构造函数)

someType getSomething() 
{
return new someType(fieldName); 
}
Run Code Online (Sandbox Code Playgroud)

从我现在所知,我的意思是在你们流行之前是:

到目前为止这是有意义的,它服务垃圾收集,因为这种方法不维护对实际对象的引用.

从内部类的角度来看,它也是可以理解的,任何方法都可以通过引用改变任何可访问的字段.

但我并不怀疑这样做的两个原因是什么才真正超越了这个问题.

Jon*_*eet 19

当类型是可变的时,返回副本通常很有用,这样客户端就不能从根本上修改数据,至少在没有告诉你的情况下.考虑:

public class Person {
    private Date dateOfBirth;

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(Date dateOfBirth) {
        // Do some validation, e.g. that it's after 1800
        this.dateOfBirth = dateOfBirth;
    }
}
Run Code Online (Sandbox Code Playgroud)

看起来没问题吧?但是关于:

Person person = new Person();
person.setDateOfBirth(new Date()); // Now... fine.
// Aha! Modify the Date to a very long time ago. Now anyone else
// using the Person will be messed up...
person.getDateOfBirth().setTime(Long.MIN_VALUE);
Run Code Online (Sandbox Code Playgroud)

如果getDateOfBirth改为返回副本,则调用者Date对返回值引用的对象所做的任何更改都将与其他任何人无关.该Person对象仍然有效,因为它只有一个有效的日期.当然,这应该被记录下来,以便编写上述代码的人会因为返回副本而不希望它影响Person对象.

一个更好的比所有这些复制的解决方案是有利于稳定的类型,但是-当你有一个不可变对象的引用,你可以共享尽可能广泛,只要你喜欢,知道没有人能在你的脚下改变其状态.

  • @Cole:当你说"考虑这是C#"时 - 这个问题是关于Java,而不是C#. (2认同)

Era*_*ran 7

我们的想法是,getter允许您查看对象的状态而无法对其进行修改(因为您将修改副本,而不是原始副本).

如果你打电话:

someType property = someObj.getSomething();
Run Code Online (Sandbox Code Playgroud)

然后

property.setSomeSubProperty(someValue);
Run Code Online (Sandbox Code Playgroud)

这只会改变副本someType,而不是原来someType存储的副本someObj.

如果包含该getSomething()方法的类是可变的,则它可能有一个setSomething(someType value)方法,并且使用该方法将是修改该属性的可接受方法.

  • @AndrewTobey字段是否为私有并不重要.getter是公共的,如果你没有在getter中返回一个副本,它会为你提供一个你可以修改的字段的引用. (2认同)