首页 > 解决方案 > 代理和 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();
    }
}

我想问的是为什么当代码运行指令instance.run(new IOException())时,它会抛出UndeclaredThrowableException?

根据 oracle 的文档,在 InvocationHandler 的调用方法中,如果该方法抛出了一个检查的异常,该异常不能分配给接口方法的 throws 子句中声明的任何异常类型,则一个 UndeclaredThrowableException 包含由抛出的异常此方法将由代理实例上的方法调用抛出。

在这种情况下,IOException 对 IOException 是可以处理的,那么为什么它会抛出 UndeclaredThrowableException 而不是 IOException?

标签: java

解决方案


因为当您从具有相似方法签名的两个接口创建Proxy时,这些方法签名将合并为一个满足两个接口要求的签名。

由于EOFException从方法签名继承IOException将是

public Object run(Throwable toThrow) throws EOFException

您可以通过创建虚拟类来自己测试:

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;
    }
}

这是因为你可以缩小抛出的异常(用覆盖throws IOException很好throws EOFException),但你不能扩大它(用覆盖throws EOFExceptionthrows IOException非法的)。

因此,当您调用该方法并EOFException抛出 a 时,它符合代理生成的方法签名(已声明)并且一切都很好。但是,当您抛出IOException它时,它不符合签名(未声明)并且您得到UndeclaredThrowableException.


推荐阅读