【问题标题】:Why friend directive is missing in Java?为什么Java中缺少朋友指令?
【发布时间】:2011-06-06 13:32:43
【问题描述】:

我想知道为什么 Java 的设计没有 C++ 中可用的 frienddirective,以便更好地控制哪些方法和实例变量可从定义类的包外部获得。

我没有看到任何实际原因或任何具体缺点,这似乎只是一个设计问题,但如果添加到语言中不会造成任何问题。

【问题讨论】:

    标签: java oop friend-class


    【解决方案1】:

    完全同意spaceghost在his answer的说法

    与流行观点相反,这里有很多情况,特别是对于基础设施功能,朋友访问会带来更好的设计,而不是更差的设计。

    我的例子很简单——如果一个类 A 必须在 java 中为类 B 提供一个特殊的“朋友”接口,我们必须将它们放在同一个包中。没有例外。在这种情况下,如果 A 是 B 的朋友,B 是 C 的朋友,则 A 必须是 C 的朋友,这并不总是正确的。这种“友谊传递性”打破了封装,而不是 C++ 友谊可能导致的任何问题。

    【讨论】:

      【解决方案2】:

      只有非常天真和缺乏经验的程序员才会反对朋友。当然,它可能会被滥用,但公共数据也可以,但提供了这种功能。

      与流行的观点相反,这里有很多情况,特别是对于基础设施功能,朋友访问导致更好的设计,而不是更差的设计。当一个方法被强制公开而实际上不应该公开时,通常会违反封装,但我们别无选择,因为 Java 不支持朋友。

      【讨论】:

      • 我希望我能给这个答案投票 100 倍。这种“朋友是坏人”的意识形态是危险的。
      • 我建议只有天真和没有经验的程序员认为他们需要让他们的朋友玩他们所有的私人。当有其他更简单的方法可用时,它会破坏封装并造成无休止的破坏。
      【解决方案3】:

      在我看来,某种友元特性(不一定与 C++ 非常相似)在 Java 中的某些情况下会非常有用。目前我们有包私有/默认访问黑客来允许在同一个包中紧密耦合的类之间进行协作(例如StringStringBuffer),但这会打开整个包的私有实现接口。在包之间我们有邪恶的反射黑客,这会导致一大堆问题。

      在 Java 中执行此操作有一点额外的复杂性。 C++ 在解决函数重载(和类似问题)时忽略访问限制 - 如果程序编译 #define private public 不应该做任何事情。 Java(大部分)丢弃不可访问的成员。如果需要考虑友谊,那么解决方案会更复杂且不太明显。

      【讨论】:

        【解决方案4】:

        除了前面提到的包可见性之外,Java 还提供了内部类和匿名类,它们不仅默认是朋友,而且还自动具有对包含类的引用。由于创建此类帮助程序类可能是在 C++ 中使用 friend 的唯一合理方式,Java 不需要它,因为它有另一种机制。迭代器就是一个很好的例子。

        【讨论】:

        • 内在但不偏。
        • @Vozzie,没错,但这与friend 有什么关系?
        【解决方案5】:

        只是添加到其他答案:

        Java 中有默认的包可见性。因此,您可以调用同一包邻居中的所有类。在这种情况下,您可以明确控制向邻居显示的内容 - 只是具有包可见性的成员。

        所以,它不是真正的朋友,但可以是相似的。是的,这也会导致糟糕的设计......

        【讨论】:

          【解决方案6】:

          为什么不简单地认为 Java 需要将友元类放在一起呢?包私有可见性允许同一包中的每个人访问这些成员。因此,您不仅限于明确声明的朋友,而且您允许任何(现有或未来的)朋友更改一些专门为此目的设计的成员(但不是您的私人物品)。您仍然可以完全依赖封装。

          【讨论】:

          • 这里的问题是任何类都可以声称是任何包的成员。这相当于我告诉保安我是你的朋友,然后保安让我搜查你的房间。
          • @R。 Bemrose:私有/受保护/公共/包可见性机制并非旨在成为安全功能。这是一种旨在帮助封装的编程功能。
          • @JeremyP friend 允许您建立单向关系,这在您希望允许类访问您的私人成员而不访问他们自己的成员的情况下非常方便。例如,考虑一个需要编写类的私有成员的Serializer 类。当然,您可以实现一些接口,并在实际对象上编写序列化逻辑,但如果您想完全分离关注点,那么让Serializer 类访问对象内部成员会更好。在 Java 中不能有单向关系。
          • @JeremyP 如果序列化也封装在类中,那是真的......你仍然需要对序列化进行更改......
          • @crush 序列化类的内部(私有)状态的想法从根本上被打破了。如果我创建一个 Stack 类(例如),我在内部使用数组还是链表来存储元素应该没有关系。一旦我允许你序列化我的 Stack 类的私有成员,我就不能再在不破坏向后兼容性的情况下更改实现。
          【解决方案7】:

          总的来说,我认为这是因为它增加了认知复杂性,而且它产生改进的案例数量很少。

          我想说,此刻生产中的极其庞大的 java 行数可以证明friend 关键字并不是真正的大损失:)。

          请参阅@dwb 的回答以了解更具体的原因。

          【讨论】:

          • 目前生产中的 Java 行数极其庞大,可以证明 Java 中的任何随机设计选择都是天经地义的。
          • @irreputable 不错的回归。但是我是在开玩笑:)
          • friend 以包可见性的形式存在。它在 Java 中没有那么灵活。
          【解决方案8】:

          以下是我想不到的几个原因:

          • 朋友不是必需的。这很方便,但不是必需的
          • 朋友支持不好的设计。如果一个班级需要朋友访问另一个班级,那你就错了。 (见上文,方便,不需要)。
          • 朋友破坏了封装。基本上,我所有的隐私都属于我,还有那边的那个人(我的朋友)。

          【讨论】:

          • –1 用于封装。这是一个常见的误解。这是错误的。虽然friend 确实可以用来破坏封装,但其他功能也可以被滥用。正确使用,friend 增强 封装,因为它支持更细粒度的访问控制:friend 替换了 public 的使用,而不是 private 的使用。 Technical explanation at the C++ FAQ(也就是说,我对包的可见性完全满意,但声称 friend 破坏封装仍然是错误的。)
          • @KonradRudolph 完全同意。友元类对于关注点分离是必不可少的。例如,我有一个 Serializer 类应该被允许写入实例的字段,所以我给它朋友访问权限。其他类,不能专门写入字段,必须通过我提供的接口。在 Java 中可以做到这一点的唯一方法是将所有内容放入同一个包中。
          • @crush - 我可以想出十几种没有朋友的方法来解决这个问题,但为什么不让你需要序列化的类实现一个序列化接口。简单,不需要朋友,它甚至可以使用您的通用 Serializer 类来实现。
          • @JoeZitzelberger 向 POJO 对象添加序列化逻辑?不,这不是一个好的解决方案。我猜你正在谈论的另一种方法是使用反射,因为这是在不使用序列化代码装饰你的 POJO 对象的情况下做我正在谈论的事情的唯一另一种方法。反射在每种情况下并不总是足够有效,但在大多数情况下就足够了。
          • 从类的角度打破封装,但不是从命名空间的角度。有时您希望将事物封装到命名空间中,而不仅仅是一个类。
          猜你喜欢
          • 2011-04-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-10
          相关资源
          最近更新 更多