mimicry in action – dynamically implement an interface using Dynamic Proxy

do you ever had that problem, that a class asks for a certain interface that your client class hasn’t implemented, although all of the interfaces required methods are present in that class?

to make things clear, here’s a little example:first of all, here’s a ‘typical’ interface (i deliberately use the prefix I to highlight that this one is an interface):

public interface IBar {
    public String echo( String s ); }

and here’s a class, that will work with that interface:

public class BarCollaborator {
    public void speakTo( IBar bar ){
        System.out.println( "hello bar - " + bar.echo( "hello" ) );
    }
}

now you have a class Foo, that offers (by incident) a method with the same signature like the one required by the interface:

public class Foo {
    public String echo(String s) {
        return "foo " + s;
    }
}

although Foo accomplishes the contract of IBar, we can’t apply an instance of Foo to BarCollaborator, because of the incompatible types.

if you use a language like ruby, were a class doesn’t rely on a certain type of a collaborator but rather on its interface (the methods an object offers – most of you will know that feature as ‘duck typing’: if it walks like a duck and if it talks like a duck than we will treat it like a duck), you certainly never will have a problem with that constellation.
but java is a different beast, using statically typed classes, where the type stays relevant during runtime. imagine if we could say something like this:

‘apply this instance of Foo to BarCollaborator and make it appear as if it implements interface IBar’

in essence – something like

‘use myFoo as IBar.class’

DynamicProxy to the rescue

well, this is possible – to a certain extend – with the help of a DynamicProxy. A DynamicProxy will ‘mimic’ a collection of interfaces (in this case we only need to mimic a single interface. in our conrete situation interface IBar ) to a client who will perform against those interfaces (in our concrete situation class BarCollaborator). when a client calls a method on that interface at runtime, the Proxy is called instead, having the chance to intercept the call and do whatever is necessery to complete it. regarding our context, we only try to detect the corresponding method on the target object (the instance of class Foo) and invoke it.

now here’s the first simple implementation:

public class InterfaceBridgeProxy implements InvocationHandler {

    // factory method
    public static <T> T as( Class<T> asInterface, Object bean ){
        return
            (T) Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    new Class[]{asInterface},
                    new InterfaceBridgeProxy( bean ) );
    }

    private Object proxee = null;

    public InterfaceBridgeProxy( Object proxee ){
        this.proxee = proxee;
    }

    public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
        Method toInvoke = findMatchingMethod( method );
        return toInvoke != null? toInvoke.invoke( proxee, args ) : null;
    }

    private Method findMatchingMethod( Method method ){
        try {
            return
                proxee.getClass().getDeclaredMethod(
                    method.getName(),
                    method.getParameterTypes() );
        }
        catch( Exception e) {
            return new RuntimeException(e);
        }
    }
}

now we can call BarCollaborator and apply an instance of Foo. using a static import for InterfaceBridgeProxy’s static method as() will make the code a little more concise:

public static void main(String[] args) {
    BarClient barClient = new BarClient();
    Foo foo = new Foo();
    barClient.speakTo( as( IBar.class, foo ) );
}

of course we could apply a generic method as() directly to class Foo …

public class Foo {
    public String echo(String s) {
        return "foo " + s;
    }

    public <T> T as( Class<T> interfaceMask ){
        return InterfaceBridgeProxy.as( interfaceMask );
    }
}

… making the example a little more readable:

...
barClient.speakTo( foo.as( IBar.class ) )

the implemented functionality is general. now you are able to mimic an arbitrary interface for your collaborators, as long as your class offers the same methods required by the interface.

next time we will go one step further and see how to extend this mechanism to even map methods of your class to an interface which even has slightly different method names as long as the rest of the signature is matching.

stay tuned … :o)

Advertisements
Posted in java. 2 Comments »

2 Responses to “mimicry in action – dynamically implement an interface using Dynamic Proxy”

  1. Anonymous Says:

    In case you’re interested, there is an existing open source library, JRetrofit, which does the thing you were talking about:

    http://www.laughingpanda.org/projects/jretrofit/

  2. Mario Gleichmann Says:

    thanks for your comment. i’ll take a look … :o)


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: