【发布时间】:2011-06-06 13:32:43
【问题描述】:
我想知道为什么 Java 的设计没有 C++ 中可用的 frienddirective,以便更好地控制哪些方法和实例变量可从定义类的包外部获得。
我没有看到任何实际原因或任何具体缺点,这似乎只是一个设计问题,但如果添加到语言中不会造成任何问题。
【问题讨论】:
标签: java oop friend-class
我想知道为什么 Java 的设计没有 C++ 中可用的 frienddirective,以便更好地控制哪些方法和实例变量可从定义类的包外部获得。
我没有看到任何实际原因或任何具体缺点,这似乎只是一个设计问题,但如果添加到语言中不会造成任何问题。
【问题讨论】:
标签: java oop friend-class
完全同意spaceghost在his answer的说法
与流行观点相反,这里有很多情况,特别是对于基础设施功能,朋友访问会带来更好的设计,而不是更差的设计。
我的例子很简单——如果一个类 A 必须在 java 中为类 B 提供一个特殊的“朋友”接口,我们必须将它们放在同一个包中。没有例外。在这种情况下,如果 A 是 B 的朋友,B 是 C 的朋友,则 A 必须是 C 的朋友,这并不总是正确的。这种“友谊传递性”打破了封装,而不是 C++ 友谊可能导致的任何问题。
【讨论】:
只有非常天真和缺乏经验的程序员才会反对朋友。当然,它可能会被滥用,但公共数据也可以,但提供了这种功能。
与流行的观点相反,这里有很多情况,特别是对于基础设施功能,朋友访问导致更好的设计,而不是更差的设计。当一个方法被强制公开而实际上不应该公开时,通常会违反封装,但我们别无选择,因为 Java 不支持朋友。
【讨论】:
在我看来,某种友元特性(不一定与 C++ 非常相似)在 Java 中的某些情况下会非常有用。目前我们有包私有/默认访问黑客来允许在同一个包中紧密耦合的类之间进行协作(例如String 和StringBuffer),但这会打开整个包的私有实现接口。在包之间我们有邪恶的反射黑客,这会导致一大堆问题。
在 Java 中执行此操作有一点额外的复杂性。 C++ 在解决函数重载(和类似问题)时忽略访问限制 - 如果程序编译 #define private public 不应该做任何事情。 Java(大部分)丢弃不可访问的成员。如果需要考虑友谊,那么解决方案会更复杂且不太明显。
【讨论】:
除了前面提到的包可见性之外,Java 还提供了内部类和匿名类,它们不仅默认是朋友,而且还自动具有对包含类的引用。由于创建此类帮助程序类可能是在 C++ 中使用 friend 的唯一合理方式,Java 不需要它,因为它有另一种机制。迭代器就是一个很好的例子。
【讨论】:
friend 有什么关系?
只是添加到其他答案:
Java 中有默认的包可见性。因此,您可以调用同一包邻居中的所有类。在这种情况下,您可以明确控制向邻居显示的内容 - 只是具有包可见性的成员。
所以,它不是真正的朋友,但可以是相似的。是的,这也会导致糟糕的设计......
【讨论】:
为什么不简单地认为 Java 需要将友元类放在一起呢?包私有可见性允许同一包中的每个人访问这些成员。因此,您不仅限于明确声明的朋友,而且您允许任何(现有或未来的)朋友更改一些专门为此目的设计的成员(但不是您的私人物品)。您仍然可以完全依赖封装。
【讨论】:
friend 允许您建立单向关系,这在您希望允许类访问您的私人成员而不访问他们自己的成员的情况下非常方便。例如,考虑一个需要编写类的私有成员的Serializer 类。当然,您可以实现一些接口,并在实际对象上编写序列化逻辑,但如果您想完全分离关注点,那么让Serializer 类访问对象内部成员会更好。在 Java 中不能有单向关系。
总的来说,我认为这是因为它增加了认知复杂性,而且它产生改进的案例数量很少。
我想说,此刻生产中的极其庞大的 java 行数可以证明friend 关键字并不是真正的大损失:)。
请参阅@dwb 的回答以了解更具体的原因。
【讨论】:
friend 以包可见性的形式存在。它在 Java 中没有那么灵活。
以下是我想不到的几个原因:
【讨论】:
friend 确实可以用来破坏封装,但其他功能也可以被滥用。正确使用,friend 增强 封装,因为它支持更细粒度的访问控制:friend 替换了 public 的使用,而不是 private 的使用。 Technical explanation at the C++ FAQ(也就是说,我对包的可见性完全满意,但声称 friend 破坏封装仍然是错误的。)
Serializer 类应该被允许写入实例的字段,所以我给它朋友访问权限。其他类,不能专门写入字段,必须通过我提供的接口。在 Java 中可以做到这一点的唯一方法是将所有内容放入同一个包中。