返回对象引用的最佳实践

8

考虑以下代码片段:

class MyClass{
    private List myList;
    //...
    public List getList(){
        return myList;
    }
}

作为Java将对象引用按值传递的方式,我的理解是任何调用getList()的对象都会获得对myList的引用,使其能够修改myList,尽管它是private。这是否正确?如果正确,我应该使用什么?
return new LinkedList(myList);

如何创建一个副本并返回对该副本的引用,而不是原始数据的引用,以防止未经授权访问由myList引用的列表?


2
从技术上讲,没有外部客户端能够更改你的类中的myList字段的值。它将始终是一个指向List的句柄/引用,引用了MyClass内部某些代码设置的列表。外部客户端将能够修改该列表的内容。 - Miro A.
是的,那就是我的意思,但是我解释得有点马虎。我会编辑它。 - chrisbunney
+1 Miro A 对于正确的澄清。是的,您创建列表副本的解决方案是正确的(针对您想要实现的目标)。 - xagyg
5个回答

9

我会这样做。更好的方法是,有时我会使用Collections API返回一个不可修改的副本。

如果你不这样做,那么你的引用就不是私有的。任何拥有引用的人都可以更改你的私有状态。对于任何可变引用(例如,日期),同样适用。


3
如果您提供了原始集合的不可修改视图,请注意原始列表中的任何更改都将反映在该视图中。如果有不可变的集合,生活会变得更加简单... - Tom Hawtin - tackline

2

这取决于你想要什么。

你想公开列表并使人们可以编辑它吗?

还是你想让人们查看它,但不能修改它?

在这种情况下,没有对错之分。这只取决于你的设计需求。


因此,如果您的设计规定属性具有私有可见性,则应返回对象的副本而不是简单引用,以确保它保持封装。 - chrisbunney
@chris 如果你的设计要求人们不能修改你的列表,则传递一个副本。否则传递原始引用。 - jjnguy
1
是的,抱歉,那只是一个修辞性的评论,我理解了你的回答 ;) - chrisbunney

1

有时候,某些情况下我们可能想要将“原始”列表返回给调用者。但总的来说,我认为这是一种不好的做法,因为它会破坏封装性,违反了面向对象编程的原则。

如果你必须返回“原始”列表而不是副本,那么应该明确告知 MyClass 的用户。


1
是的,而且它有一个名字...“防御性拷贝”。在接收端进行拷贝也是推荐的做法。正如Tom所指出的, 如果集合是不可变的,程序的行为更容易预测。所以除非你有非常充分的理由,否则应该使用不可变的集合。
Google Guava成为Java标准库的一部分时(我完全认为它应该成为),这可能会成为首选的习惯用法。
return ImmutableList.copyOf(someList);

并且

void (List someList){
    someList = ImmutableList.copyOf(someList);

这样做的额外好处是提高了性能,因为copyOf()方法会检查集合是否已经是不可变集合的实例(instanceof ImmutableList),如果是,则跳过复制。

1

我认为将字段设为私有并提供访问器的模式只是为了数据封装。如果你想要真正的私有性,就不要提供访问器方法!然后你可以编写其他方法来返回你的私有数据的不可变版本或其副本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接