Рассмотрим этот фрагмент кода java 8:
public class Generics {
public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
return callable.call();
}
public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
return callable.call();
}
public static void main(String[] args) {
f(() -> 1);
g(() -> 1);
}
}
interface Callable<V> {
V call() throws Exception;
}
interface CheckedCallable1<V, E extends Exception> {
V call() throws E;
}
interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
@Override V call() throws E;
}
Лямбда при вызове f
компилируется отлично, тогда как лямбда при вызове g
не компилируется, а скорее дает эту ошибку компиляции:
Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
overridden method does not throw java.lang.Exception
Почему это?
Мне кажется, что оба метода CheckedCallable1.call
и CheckedCallable2.call
эквивалентны: по правилам стирания типа V
становится Object
, поскольку он неограничен, а E
становится Exception
, as что верхний тип связан. Итак, почему компилятор считает, что переопределенный метод не бросает java.lang.Exception?
Даже игнорируя стирание типа, которое, вероятно, не имеет значения здесь, потому что это происходит во время компиляции, мне все равно не имеет смысла: я не вижу причины, почему этот шаблон, если он разрешен, приведет к тому, скажем, неправильный код Java.
Так кто-нибудь может рассказать мне, почему это не разрешено?
Update:
Итак, я нашел кое-что, что может быть еще интереснее. Возьмите вышеупомянутый файл, измените каждое вхождение Exception
на IOException
и добавьте предложение throw к main
. Компиляция работает! Вернитесь к Exception
: скомпилируйте breaks!
Это компилируется отлично:
import java.io.IOException;
public class Generics {
public static <V, E extends IOException> V f(CheckedCallable1<V, E> callable) throws E {
return callable.call();
}
public static <V, E extends IOException> V g(CheckedCallable2<V, E> callable) throws E {
return callable.call();
}
public static void main(String[] args) throws IOException {
f(() -> 1);
g(() -> 1);
}
}
interface Callable<V> {
V call() throws IOException;
}
interface CheckedCallable1<V, E extends IOException> {
V call() throws E;
}
interface CheckedCallable2<V, E extends IOException> extends Callable<V> {
@Override V call() throws E;
}
В этот момент он начинает все больше выглядеть как ошибка java...