speed9
speed9

Reputation: 89

proxy and UndeclaredThrowableException

import java.io.EOFException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public class ProxyDemo {
    private final ClassLoader loader = getClass().getClassLoader();
    private final InvocationHandler throwHandler = new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            throw (Throwable) args[0];
        }
    };
    public interface ThrowsIOException {
        Object run(Throwable toThrow) throws IOException;
    }

    public interface ThrowsEOFException {
        Object run(Throwable toThrow) throws EOFException;
    }

    public void testDeclaredExceptionIntersectionIsSubtype() throws Exception {
        ThrowsIOException instance = (ThrowsIOException) Proxy.newProxyInstance(loader,
                new Class[] {ThrowsIOException.class, ThrowsEOFException.class}, throwHandler);
        try {
            instance.run(new EOFException());
        } catch (EOFException expected) {
        }
        try {
            instance.run(new IOException());
        } catch (UndeclaredThrowableException expected) {
        }
        try {
            instance.run(new Exception());
        } catch (UndeclaredThrowableException expected) {
        }
    }

    public static void main(String[] args) throws Throwable {
        ProxyDemo cl = new ProxyDemo();
        cl.testDeclaredExceptionIntersectionIsSubtype();
    }
}

all i want to ask is why when the code run the instruction instance.run(new IOException()), it will throw UndeclaredThrowableException?

according to the oracle's doc,in InvocationHandler's invoke method,If a checked exception is thrown by this method that is not assignable to any of the exception types declared in the throws clause of the interface method, then an UndeclaredThrowableException containing the exception that was thrown by this method will be thrown by the method invocation on the proxy instance.

in this case, IOException is assinable to IOException, so why it throws UndeclaredThrowableException instead of IOException?

Upvotes: 1

Views: 5477

Answers (1)

Max Vollmer
Max Vollmer

Reputation: 8598

Because when you create a Proxy from two interfaces that have similar method signatures, these method signatures will be merged into one signature that fulfills the requirements for both interfaces.

Since EOFException inherits from IOException the method signature will be

public Object run(Throwable toThrow) throws EOFException

You can test this yourself by creating dummy classes:

public class Test implements ThrowsIOException, ThrowsEOFException {
    @Override
    public Object run(Throwable toThrow) throws IOException // compiler error
    {
        return null;
    }
}

public class Test implements ThrowsIOException, ThrowsEOFException {
    @Override
    public Object run(Throwable toThrow) throws EOFException // valid
    {
        return null;
    }
}

This is because you can narrow the exception being thrown (overriding throws IOException with throws EOFException is fine), but you cannot widen it (overriding throws EOFException with throws IOException is illegal).

Thus when you invoke the method and a EOFException is thrown, it fits the proxy's generated method signature (it is declared) and everything is fine. However when you throw IOException it doesn't fit the signature (it's not declared) and you get UndeclaredThrowableException.

Upvotes: 1

Related Questions