Java中防御性复制的低效问题

5

我是一名长期从事C/C++编程的程序员,正在学习Java。我了解到通过返回私有字段的引用来访问器方法会破坏封装的问题,标准的Java解决方案似乎是进行防御性复制 - 调用复制构造函数或clone()来创建字段的副本,并返回对副本的引用。但我不明白为什么没有人关注防御性复制的低效率。在C++中,访问器只需返回指向const的指针,而无需复制即可保护私有成员。为什么Java没有一个指向const的引用?


请提供数据以说明其来源。我不知道有任何这样的“标准Java解决方案”。它取决于数据类型和其他因素。例如,字符串是不可变的,因此复制没有意义。 - Brad Peabody
2
那就是我做的事。否则你可以把那个私有变量公开化,别再欺骗自己了。至于“为什么”,Java中没有常量引用。得问Gosling和Joy。 - duffymo
Java确实有类似于const引用的东西。对于返回集合的访问器,应该使用Collections类中的一整套方法。这些方法的名称类似于unmodifiableListunmodifiableMap等,它们会产生一个对集合的引用,而不是复制它,但也不允许更改集合。 - Dawood ibn Kareem
@duffymo - 我不确定(比尔)乔伊与此有何关系... - Stephen C
只是另一个可能知道答案的Sun聪明人。 - duffymo
2个回答

1
为什么Java没有const的引用?
只有语言设计者才能正确回答这个问题,但我认为问题在于他们无法想出如何将其作为语言设计的一部分使其正常工作。据我记得(从某次偶然发现的“Java设计原理”文档中),Gosling等最初想支持const...
事实上,虽然C和C++都支持const作为表达可变性约束的方法,但它们也都有漏洞,允许一些代码“破坏”约束。 (参见维基百科上的const-correctness文章。)可能正是想要在Java中设计一个没有(或不需要)此类漏洞的方案,导致Gosling等人放弃了这个想法。
另一方面,Java中需要进行防御性复制的需求并不像你想象的那么大,并且执行它的成本也不像你想象的那么高。而当防御性拷贝的成本很高时,Java还有其他选择...例如创建“不可修改”的包装对象,或仅支持“读取”操作的接口。

在const-correctness方面提出了很好的观点。在C/C++中,可能会做一些导致运行时状态与编译时“保证”不一致的事情(将const从类型中转换出来,不安全地转换指针类型等)。在Java中,故意不允许这些事情发生 - 所以这个论点非常有道理。 - Brad Peabody

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行代码。

如果您确实想确保没有人修改您发送回来的内容,请不要发送可变的内容。 (并且没有理由通过低效的深度复制来完成,如上所述。)


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