How do I make a class, which I can't change, implement an interface?
adapter, instanceof, interface, java, proxy-classes
Solution
There are two approaches:
- Using an adapter pattern
- Using `Proxy`
The adapter approach will be simpler but less flexible, and the `Proxy` approach will be more complex but more flexible. Even though the `Proxy` approach is more complex, that complexity is all restricted to a couple of classes.
Adapter
The adapter pattern is simple. For your example, it would just be one class, like so:
public class QuietFooAdapter implements Foo {
private QuietFoo quietFoo;
public QuietFooAdapter(QuietFoo quietFoo) {
this.quietFoo = quietFoo;
}
public void bar() {
quietFoo.bar();
}
}
Then to use it:
Foo foo = new QuietFooAdapter(new QuietFoo());
foo.bar();
This is good, but if you have more than one class to make an adapter for, this can be tedious since you'll need a new adapter for each class you have to wrap.
Java's `Proxy` Class
`Proxy` is a native Java class that's part of the reflection libraries that will allow you to create a more generic, reflective solution. It involves 3 parts:
- The interface (in this case, `Foo`)
- The `InvocationHandler`
- Creation of the proxy (`Proxy.newProxyInstance`)
We already have the interface, so we're fine there.
The `InvocationHandler` is where we do our "auto-adapting" via reflection:
public class AdapterInvocationHandler implements InvocationHandler {
private Object target;
private Class<?> targetClass;
public AdapterInvocationHandler(Object target) {
this.target = target;
targetClass = target.getClass();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
if (!method.getReturnType().isAssignableFrom(targetMethod.getReturnType()))
throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString());
return targetMethod.invoke(target, args);
} catch (NoSuchMethodException ex) {
throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString());
} catch (IllegalAccessException ex) {
throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not declare method to be public: " + method.toGenericString());
} catch (InvocationTargetException ex) {
// May throw a NullPointerException if there is no target exception
throw ex.getTargetException();
}
}
}
The important code here is in the `try` block. This will handle the process of adapting any method calls that are called on the proxy to the inner `target` object. If a method is called on the interface that isn't supported (non-`public`, wrong return type, or just flat out doesn't exist), then we throw an `UnsupportedOperationException`. If we catch an `InvocationTargetException`, we rethrow the exception that caused it via `InvocationTargetException.getTargetException`. This occurs when the method we called reflectively throws an exception. Java wraps it in a new exception and throws that new exception.
Next, we need something to create the adapters:
public class AdapterFactory {
public static <T> T createAdapter(Object target, Class<T> interfaceClass) {
if (!interfaceClass.isInterface())
throw new IllegalArgumentException("Must be an interface: " + interfaceClass.getName());
return (T) Proxy.newProxyInstance(null, new Class<?>[] { interfaceClass }, new AdapterInvocationHandler(target));
}
}
You could also nest the `AdapterInvocationHandler` class in the `AdapterFactory` class, if you like, so that everything is self-contained in `AdapterFactory`.
Then to use it:
Foo foo = AdapterFactory.createAdapter(new QuietFoo(), Foo.class);
foo.bar();
This approach requires more code than implementing a single adapter, but will be generic enough that it can be used to create auto-adapters for any class and interface pair, not just the `QuietFoo` and `Foo` example. Granted, this method uses reflection (the `Proxy` class uses reflection, as does our `InvocationHandler`), which can be slower, but recent improvements in the JVM have made reflection much faster than it used to be.
Problem
I have a class from another library that is closed-source, but I want to be able to use an interface for it. The reason being that I don't want to do `instanceof` checks or `null`-checks everywhere, but I also don't want to extend the existing class. For example, let's say I have this code: ``` public class Example { // QuietFoo is from another library that I can't change private static QuietFoo quietFoo; // LoudFoo is my own code and is meant to replace QuietFoo private static LoudFoo loudFoo; public static void main(String[] args) { handle(foo); } private static void handle(Object foo) { if (foo instanceof QuietFoo) ((QuietFoo) foo).bar(); else if (foo instanceof LoudFoo) ((LoudFoo) foo).bar(); } } ``` I can't change `QuietFoo`: ``` public class QuietFoo { public void bar() { System.out.println("bar"); } } ``` But I can change `LoudFoo`: ``` public class LoudFoo { public void bar() { System.out.println("BAR!!"); } } ``` The problem is, there may be many other implementations of `bar` in many classes, and there may be more methods than just `bar`, so not only would my `handle` method get slow and ugly with lots of `instanceof` statements, but I would have to write one of these handle methods for each method on `QuietFoo` and `LoudFoo`. Extending isn't a viable solution because it violates the whole is-a contract since `LoudFoo` is not a `QuietFoo`. Basically, given `Foo`: ``` public interface Foo { void bar(); } ``` How can I make `QuietFoo` implement `Foo` without changing its source so I don't have to do casting and `instanceof` calls everywhere in my code?