Using a third-party abstract class, as if it were an interface?

In the third-party library that I use, the following bit of the hierarchy exists:

public abstract class Foo { // Class is public in the Java sense and public in the library API } public class FooImpl extends Foo { // While this class is public in the Java sense, it is // NOT part of the public API and is not even documented // Library functions that hand back instances of this class // hand them back as type Foo } 

Various methods in the library work with things like Foo . Foo docs say that users can extend Foo to make a specific implementation, and (obviously) the library provides a specific implementation.

I am pleased with the implementation of the library, except for one method whose behavior I would like to change a little. But the hierarchy that was provided to me disconnects me.

If Foo was an interface (it’s not!), I would just do this:

 public FooWrapper implements Foo { private Foo wrappedImpl; public FooWrapper(Foo toBeWrapped) { wrappedImpl = toBeWrapped; } List<Whatever> methodIWantToTweak() { List<Whatever> list = wrappedImpl.methodIWantToTweak(); do_something_to_list(list); return list; } Something methodThatsOk() { return wrappedImpl.methodThatsOk(); } // similar delegations for remaining methods } 

But since this is not an interface, I cannot do this, or at least I cannot do it as cleanly as possible.

The only thing I can come up with is:

1) Extend the FooImpl API non-public class and override the method I want to configure.

2) Do something like:

 public class FooWrapper extends Foo { private Foo wrappedImpl; public FooWrapper(Foo toBeWrapped) { super(); wrappedImpl = toBeWrapped; } List<Whatever> methodIWantToTweak() { List<Whatever> list = wrappedImpl.methodIWantToTweak(); do_something_to_list(list); return list; } Something methodThatsOk() { return wrappedImpl.methodThatsOk(); } // Override all non-final public, protected, and package-local // methods and delegate them to wrappedImpl } 

Both of these approaches smell like hell. The first problem is that it depends on a class that it should not be aware of, and which may be changed / disappeared / renamed in future versions of the library. The second problem is that a piece of the FooWrapper superclass is FooWrapper actually used and that it is impossible to override any final or private Foo methods (which will cause problems, since wrappedImpl cannot be delegated in those cases).

I think I will have to go with the first approach, because at least it will give the correct behavior, while the second will most likely be broken in a potentially evil, subtle way depending on the internal details of Foo .

Am I really out of luck? What other approaches / ideas am I missing?

+4
source share
2 answers

I cannot think of any other approach that would not be much more smelly! (Reflection will work, but in this case it will be worse than the problem you are trying to avoid.)

I think the second approach is the best. If the abstract class Foo designed as an open class, then it should not have any unpleasant internal details that can raise you. If so, they have the wrong design.

Note that since you are creating a wrapper class for "real" instances of Foo:

  • You can probably ignore most of the functions that the abstract class provides for the β€œreal” subclasses of Foo.

  • Perhaps you can ignore any state represented by attributes of the Foo level.

  • You can use API methods that you do not need to use in your application; for example, encode them to throw an UnsupportedOperationException .

All this helps reduce the complexity of your problem.

+2
source

If you don’t need to redefine any of the Foo final or protected (see below) methods, I would go with the second approach: treat Foo as an interface and do what you do in that. The advantage is that you are not dependent on FooImpl , which may change.

final and private methods of Foo are not a problem, since you cannot override the ones that are in FooImpl , or if Foo was an interface, anyway. Also, I think you meant protected instead of private .

If you need to override protected methods, you will need to move on to the first approach.

+3
source

Source: https://habr.com/ru/post/1414652/


All Articles