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?
в соответствии с документом оракула в методе invoke InvocationHandler, если этим методом генерируется проверенное исключение, которое не может быть назначено ни одному из типов исключений, объявленных в предложении throws метода интерфейса, тогда UndeclaredThrowableException, содержащее исключение, которое было сгенерировано этот метод будет сгенерирован при вызове метода в экземпляре прокси.
в этом случае IOException присваивается IOException, так почему он выбрасывает UndeclaredThrowableException вместо IOException?




Потому что, когда вы создаете Прокси из двух интерфейсы, которые имеют одинаковые сигнатуры методов, эти сигнатуры методов будут объединены в одна подпись, который удовлетворяет требованиям для обоих интерфейсов.
Поскольку 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 EOFException с помощью throws IOException незаконно).
Таким образом, когда вы вызываете метод и генерируется EOFException, он соответствует сгенерированной подписи метода прокси (она объявлена), и все в порядке. Однако, когда вы бросаете IOException, он не подходит под подпись (не декларируется), и вы получаете UndeclaredThrowableException.
newProxyInstance выкинет IllegalArgumentExceptionЯ нашел ответ в документе оракула, он о «методах, дублируемых в нескольких интерфейсах прокси», но все же спасибо за вашу помощь.
спасибо, а что будет если два интерфейса не могут быть объединены?