我是一名长期学习 Java 的 C/C++ 程序员。我已经阅读了通过使用返回对私有字段的引用的访问器方法来破坏封装的问题。标准的 Java 解决方案似乎是防御性复制——调用复制构造函数或 clone() 来创建字段的副本并返回对副本的引用。我不明白为什么似乎没有人担心制作防御性副本的效率低下。在 C++ 中,访问器只会返回一个指向 const 的指针,从而保护私有成员而不进行复制。为什么 Java 没有对 const 的引用?
2 回答
为什么 Java 没有对 const 的引用?
问题只能由语言设计者正确回答,但我认为问题在于他们无法弄清楚如何使其成为语言设计的一部分。我的回忆(来自我曾经遇到的一些“Java 设计原理”文档)是 Gosling 等人最初想要支持const
...
事实上,尽管 C 和 C++ 都支持const
作为表达可变性约束的一种方式,但它们都存在允许某些代码“打破”约束的漏洞。(参见关于const-correctness的 Wikipedia 文章。)可能是因为难以为 Java 设计一个没有(或不需要)这样的漏洞,导致 Gosling 等人放弃了这个想法。
另一方面,Java 中防御性复制的需求并没有你想象的那么大,而且这样做的成本也没有你想象的那么大。当防御性副本的成本很高时,Java 中还有其他选择……比如创建“不可修改的”包装对象,或者只支持“读取”操作的接口。
注意:我不知道您会直接回答为什么const
Java 中没有这个问题。(我的猜测是因为 Java 的动态特性,使用 const 关键字并没有提供尽可能多的编译器优化,因此被认为不值得投入 - 但这只是我的看法。[在 C++ 中,你拥有所有最终的具体类型在编译时对您可用,在 Java 中,您不可用。])
至于通常会做什么,您必须根据数据类型和您的字段在语义上的含义做出决定。
一些常见的类型(String、Date)是不可变的,旨在以尽可能少的开销从 getter 传递和返回,同时不允许修改。
正如@DavidWallace 指出的那样,有些方法可以创建一个浅的、不可修改的 Map 副本,并允许您将其返回给调用者 - 以保证他们不会弄乱它。这是一个实用的解决方案,但它不会在编译时强制执行。
如果我们在谈论地图:根据接口协定,java.util.Map 是可变的。要实现接近 const 的功能,您可以轻松地创建一个不同但简单的接口,只需一个查找方法:
public interface Lookup<K,V> {
public V get(K);
}
并返回一个实例。因此保证没有人会修改它......在编译时......因为不存在这样的方法。
并且通过包装地图而不制作副本来实现上述实现的 MapLookup 就像 5 行代码。
如果您想确保没有人修改您发回的内容,请不要发回可变的内容。(如上所述,没有理由必须通过低效的深拷贝来完成。)