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