Title: Multiple Business Interface Hazzards # UndeclaredThrowableException When two java interfaces are implemented by a proxy and those two interfaces declare the *same method* but with *different throws clauses* some very nasty side effects happen, namely you loose the ability to throw any checked exceptions that are not in the throws clause of both methods. import junit.framework.TestCase; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; /** * @version $Rev$ $Date$ */ public class ExceptionTest extends TestCase { public void test() throws Exception { ClassLoader classLoader = this.getClass().getClassLoader(); Class[] interfaces = new Class[]{One.class, Two.class}; InvocationHandler h = new TestInvocationHandler(); Object proxy = java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, h); One one = (One) proxy; try { one.run(new CommonException()); } catch (CommonException e) { // this will work } catch(UndeclaredThrowableException u) { Throwable t = u.getCause(); fail("Undeclared: "+t); } catch(Throwable t){ fail("Caught: "+t); } try { one.run(new OneException()); } catch (OneException e) { } catch(UndeclaredThrowableException u) { Throwable t = u.getCause(); fail("Undeclared: "+t); // This will always be the code that executes } catch(Throwable t){ fail("Caught: "+t); } Two two = (Two) proxy; try { two.run(new CommonException()); } catch (TwoException e) { } catch(UndeclaredThrowableException u) { Throwable t = u.getCause(); fail("Undeclared: "+t); // This will always be the code that executes } catch(Throwable t){ fail("Caught: "+t); } } public static class CommonException extends Exception { public CommonException() { } } public static interface One { void run(Object o) throws OneException, CommonException; } public static class OneException extends Exception { public OneException() { } } public static interface Two { void run(Object o) throws TwoException, CommonException; } public static class TwoException extends Exception { public TwoException() { } } private static class TestInvocationHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { throw (Throwable)args[0] ; } } } # IllegalArgumentException This one is less of a runtime problem as doing this will cause things to fail up front. When two java interfaces are implemented by a proxy and those two interfaces declare the *same method* but with *different return types* the VM proxy code will refuse to create a proxy at all. Take this code example: import junit.framework.TestCase; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @version $Rev$ $Date$ */ public class ReturnTest extends TestCase { public void test() throws Exception { ClassLoader classLoader = this.getClass().getClassLoader(); Class[] interfaces = new Class[]{ReturnTest.One.class, ReturnTest.Two.class}; InvocationHandler h = new ReturnTest.TestInvocationHandler(); Object proxy = java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, h); One one = (One) proxy; try { Object object = one.run(new ThingOne()); } catch (Throwable t) { fail("Caught: " + t); } Two two = (Two) proxy; try { Object object = two.run(new ThingTwo()); } catch (Throwable t) { fail("Caught: " + t); } } public static interface One { ThingOne run(Object o); } public static class ThingOne { } public static interface Two { ThingTwo run(Object o); } public static class ThingTwo { } private static class TestInvocationHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return args[0] ; } } } Running this code will result in the following exception: java.lang.IllegalArgumentException: methods with same signature run(java.lang.Object) but incompatible return types: [class ReturnTest$ThingOne, class ReturnTest$ThingTwo] at sun.misc.ProxyGenerator.checkReturnTypes(ProxyGenerator.java:669) at sun.misc.ProxyGenerator.generateClassFile(ProxyGenerator.java:420) at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:306) at java.lang.reflect.Proxy.getProxyClass(Proxy.java:501) at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581) at ReturnTest.test(ReturnTest.java:36) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:32)