views:

195

answers:

3

So, I realize the answer to this is probably "it's hard", but:

I've got a weird idea, and was wondering if it's possible in Java to create a method like:

<T> T wrapInterface (Class<T> interfaceClass, T wrappedObject) {
  if (mClass.isInterface()) {
    //create a new implementation of interfaceClass that, in each method, 
    //does some action before delegating to wrappedObject
    return thatImplementation;
  }
}

So basically, if my interface Foo defined a method foo(), I'd want this method to create a new class that looked about like this, created an instance of that class with wrappedObject as the constructor parameter, and then returned it:

public class GeneratedClass implements Foo {
  private Foo wrapped;
  public GeneratedClass (Foo wrapped) {
    this.wrapped = wrapped;
  }
  @Override
  public void foo () {
    System.out.println("Calling Foo.foo() on wrapped object " + 
                        wrapped.toString());
    wrapped.foo();
  }
}

The application I'm considering is more complicated than just logging the call, but logging is sufficient for the idea. I'd like to do this with a large number of interface types, which is why I wouldn't just write all the GeneratedClasses by hand.

Bonus points for a solution that doesn't require extra-linguistic features (bringing in AspectJ or something along those lines), and double-bonus-points if this is possible with just the standard JDK libraries.

(I don't need a precise, compilable answer; just a pointer to the right sets of tools/libraries/etc that would let me do this.)

Thanks!

+10  A: 

You might want to look into dynamic proxy classes.

Hank Gay
Yep -- this type of thing is exactly what dynamic proxies are for
Scott Stanchfield
That is indeed exactly what I was looking for. Many thanks!
Sbodd
+1  A: 

Here is a very simplistic implementation (not good enough likely for what you want to do, but with some tweaking ... The main thing to look at would be class loader issues, then there might be some validation issues, etc.) I use the code for testing purposes, so it isn't exactly production quality stuff.

    @SuppressWarnings("unchecked")
    public static <T> T generateProxy(Object realObject, Class<?>... interfaces) {
        return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject));
    }


    private static class SimpleInvocationHandler implements InvocationHandler {
        private Object invokee;

        public SimpleInvocationHandler(Object invokee) {
            this.invokee = invokee;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes());
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            try {
                return method.invoke(invokee, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    }
Yishai
Thanks for the answer (and the sample code)!
Sbodd
+1  A: 

What you need is ASM.

From asm-guide.pdf:
2.2.3 Generating classes
The only required component to generate a class is the ClassWriter component. Let’s take an example to illustrate this. Consider the following interface:

package pkg;
public interface Comparable extends Mesurable {
  int LESS = -1;
  int EQUAL = 0;
  int GREATER = 1;
  int compareTo(Object o);
}

It can be generated with six method calls to a ClassVisitor:

ClassWriter cw = new ClassWriter(0);
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
    "pkg/Comparable", null, "java/lang/Object",
    new String[] { "pkg/Mesurable" });
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I",
    null, new Integer(-1)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
    null, new Integer(0)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
    null, new Integer(1)).visitEnd();
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
    "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
byte[] b = cw.toByteArray();
rodrigoap