Effective Java: Safety of Forwarding Classes

shmosel :

Effective Java 3rd Edition, Item 18: Favor composition over inheritance describes an issue with using inheritance to add behavior to a 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.

The recommended solution:

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.

My question is, doesn't the risk remain that methods could also be added to the forwarding class, thereby breaking the invariants of the subclass? How could an external library like Guava ever incorporate newer methods in forwarding classes without risking the integrity of its clients?

Thomas :

The tacit assumption seems to be that you are the one writing the forwarding class, therefore you are in control of whether anything gets added to it. That's the common way of using composition over inheritance, anyway.

The Guava example seems to refer to the Forwarding Decorators, which are explicitly designed to be inherited from. But they are just helpers to make it simpler to create these forwarding classes without having to define every method in the interface; they explicitly don't shield you from any methods being added in the future that you might need to override as well:

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.

So, if I understood all this correctly, Guava is not such a great example.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=460941&siteId=1