有效Java:转发安全类

Effective Java: Safety of Forwarding Classes

Effective Java 第 3 版,第 18 项:优先考虑组合而不是继承 描述了使用继承向 class 添加行为的问题:

A related cause of fragility in subclasses is that their superclass can acquire new methods in subsequent releases. Suppose a program depends for its security on the fact that all elements inserted into some collection satisfy some predicate. This can be guaranteed by subclassing the collection and overriding each method capable of adding an element to ensure that the predicate is satisfied before adding the element. This works fine until a new method capable of inserting an element is added to the superclass in a subsequent release. Once this happens, it becomes possible to add an "illegal" element merely by invoking the new method, which is not overridden in the subclass.

推荐方案:

Instead of extending an existing class, give your new class a private field that references an instance of the existing class... Each instance method in the new class invokes the corresponding method on the contained instance of the existing class and returns the results. This is known as forwarding, and the methods in the new class are known as forwarding methods... adding new methods to the existing class will have no impact on the new class... It's tedious to write forwarding methods, but you have to write the reusable forwarding class for each interface only once, and forwarding classes may be provided for you. For example, Guava provides forwarding classes for all of the collection interfaces.

我的问题是,方法是否也可以添加 到转发 class,从而打破子 [=25] 的不变量=]?像 Guava 这样的外部库如何在转发 classes 时合并更新的方法而不冒其客户端完整性的风险?

doesn't the risk remain that methods could also be added to the forwarding class, thereby breaking the invariants of the subclass?

组合是继承的另一种选择,所以当你使用组合时,没有sub-class。如果您向转发 class 添加新的 public 方法(可能访问包含实例的方法),这意味着您希望使用这些方法。

因为你是转发的拥有者class,只有你可以给它添加新的方法,从而保持不变。

默认的假设似乎是 是编写转发 class 的人,因此你可以控制是否向其中添加任何内容。无论如何,这是使用组合而不是继承的常见方式。

Guava 示例似乎引用了 the Forwarding Decorators,它被明确设计为从中继承。但它们只是帮手,使创建这些转发 classes 变得更简单,而不必在接口中定义每个方法;他们明确地不会保护您免受将来添加的您可能还需要覆盖的任何方法的影响:

Remember, by default, all methods forward directly to the delegate, so overriding ForwardingMap.put will not change the behavior of ForwardingMap.putAll. Be careful to override every method whose behavior must be changed, and make sure that your decorated collection satisfies its contract.

所以,如果我理解正确的话,Guava 并不是一个很好的例子。