Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There are two approaches:</p> <ol> <li>Using an adapter pattern</li> <li>Using <a href="http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html" rel="noreferrer"><code>Proxy</code></a></li> </ol> <p>The adapter approach will be simpler but less flexible, and the <code>Proxy</code> approach will be more complex but more flexible. Even though the <code>Proxy</code> approach is more complex, that complexity is all restricted to a couple of classes.</p> <hr> <p><strong>Adapter</strong></p> <p>The <a href="http://www.oodesign.com/adapter-pattern.html" rel="noreferrer">adapter pattern</a> is simple. For your example, it would just be one class, like so:</p> <pre><code>public class QuietFooAdapter implements Foo { private QuietFoo quietFoo; public QuietFooAdapter(QuietFoo quietFoo) { this.quietFoo = quietFoo; } public void bar() { quietFoo.bar(); } } </code></pre> <p>Then to use it:</p> <pre><code>Foo foo = new QuietFooAdapter(new QuietFoo()); foo.bar(); </code></pre> <p>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.</p> <hr> <p><strong>Java's <code>Proxy</code> Class</strong></p> <p><code>Proxy</code> 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:</p> <ol> <li>The interface (in this case, <code>Foo</code>)</li> <li>The <a href="http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/InvocationHandler.html" rel="noreferrer"><code>InvocationHandler</code></a></li> <li>Creation of the proxy (<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html#newProxyInstance%28java.lang.ClassLoader,%20java.lang.Class%5B%5D,%20java.lang.reflect.InvocationHandler%29" rel="noreferrer"><code>Proxy.newProxyInstance</code></a>)</li> </ol> <p>We already have the interface, so we're fine there.</p> <p>The <code>InvocationHandler</code> is where we do our "auto-adapting" via reflection:</p> <pre><code>public class AdapterInvocationHandler implements InvocationHandler { private Object target; private Class&lt;?&gt; 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(); } } } </code></pre> <p>The important code here is in the <code>try</code> block. This will handle the process of adapting any method calls that are called on the proxy to the inner <code>target</code> object. If a method is called on the interface that isn't supported (non-<code>public</code>, wrong return type, or just flat out doesn't exist), then we throw an <code>UnsupportedOperationException</code>. If we catch an <code>InvocationTargetException</code>, we rethrow the exception that caused it via <code>InvocationTargetException.getTargetException</code>. This occurs when the method we called reflectively throws an exception. Java wraps it in a new exception and throws that new exception.</p> <p>Next, we need something to create the adapters:</p> <pre><code>public class AdapterFactory { public static &lt;T&gt; T createAdapter(Object target, Class&lt;T&gt; interfaceClass) { if (!interfaceClass.isInterface()) throw new IllegalArgumentException("Must be an interface: " + interfaceClass.getName()); return (T) Proxy.newProxyInstance(null, new Class&lt;?&gt;[] { interfaceClass }, new AdapterInvocationHandler(target)); } } </code></pre> <p>You could also nest the <code>AdapterInvocationHandler</code> class in the <code>AdapterFactory</code> class, if you like, so that everything is self-contained in <code>AdapterFactory</code>.</p> <p>Then to use it:</p> <pre><code>Foo foo = AdapterFactory.createAdapter(new QuietFoo(), Foo.class); foo.bar(); </code></pre> <p>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 <code>QuietFoo</code> and <code>Foo</code> example. Granted, this method uses reflection (the <code>Proxy</code> class uses reflection, as does our <code>InvocationHandler</code>), which <em>can</em> be slower, but recent improvements in the JVM have made reflection much faster than it used to be.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload