Для следующего фрагмента кода:
import java.util.List;
public class Main {
interface Interface1<T> {}
interface Interface2<T> extends Interface1<T> {}
static class Bound {}
interface BoundedI1<T extends Bound> extends Interface1<T> {}
interface BoundedI2<T extends Bound> extends Interface2<T> {}
public static void main(String[] args) {
test((List<BoundedI2<?>>) null);
//test2((List<BoundedI2<?>>) null);
}
public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
public static void test2(List<? extends Interface1<? extends Bound>> list) {}
}
Компилятор в порядке с первым вызовом, но жалуется, если я раскомментирую второй. Является ли это ошибкой в системе вывода типов или кто-нибудь может объяснить, почему здесь не работают правила вывода в JLS?
Протестировано на Oracle JDK 6u43 и 7u45.
ОБНОВЛЕНИЕ: похоже, что eclipsec прекрасно его принимает. К сожалению, я не могу изменить наш набор инструментов :P, но интересно найти различия в компиляторах.
Сообщение об ошибке, напечатанное ideone (кстати, крутой инструмент):
Main.java:12: error: method test2 in class Main cannot be applied to given types;
test2((List<BoundedI2<?>>) null);
^
required: List<? extends Interface1<? extends Bound>>
found: List<BoundedI2<?>>
reason: actual argument List<BoundedI2<?>> cannot be converted to List<? extends Interface1<? extends Bound>> by method invocation conversion
ОБНОВЛЕНИЕ 2: это компилируется нормально, что указывает на то, что компилятор действительно считает, что BoundedI2<?>
можно присвоить Interface1<? extends Bound>
, что, по-видимому, более прямо противоречит JLS:
public class Main {
interface Interface1<T> {}
interface Interface2<T> extends Interface1<T> {}
static class Bound {}
interface BoundedI1<T extends Bound> extends Interface1<T> {}
interface BoundedI2<T extends Bound> extends Interface2<T> {}
public static void main(String[] args) {
test((List<BoundedI2<?>>) null);
//test2((List<BoundedI2<?>>) null);
test3((BoundedI2<?>) null);
}
public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
public static void test2(List<? extends Interface1<? extends Bound>> list) {}
public static void test3(Interface1<? extends Bound> instance) {}
}
javac
из командной строки, я получаю сообщение об ошибке:test2 (java.util.List<? extends Main.Interface1<? extends Main.Bound>>) in Main cannot be applied to (java.util.List<Main.BoundedI2<?>>)
. Я дважды проверил путь, который использует eclipse, чтобы убедиться, что это тот же компилятор. Это очень интересно. - person azurefrog   schedule 18.07.2014<?>
на<? extends Bound>
в закомментированной строке не приводит к ошибке. Возможно, правила вывода не учитывают такое комбинирование нескольких типов вывода, но я недостаточно знаком с правилами, чтобы сказать, так ли это. Есть еще одна проблема в том, что я не уверен, следует ли правильно воспринимать<?>
как<? extends Object>
или<? extends Bound>
, но это еще одна проблема... - person awksp   schedule 18.07.2014test()
не должно компилироваться, посколькуList<BoundedI2<?>>
нельзя присвоитьList<? extends Interface2<? extends Bound>>
? В частности,BoundedI2
расширяетInterface2
, но<?>
не является<? extends Bound>
. Я что-то упускаю? ЕслиBoundedI2
определено как<T extends Bound>
, достаточно ли умен компилятор, чтобы знать, что<?>
должно быть<? extends Bound>
? Это вообще обоснованное заявление? - person awksp   schedule 18.07.2014<?>
действительно означает<? extends Bound>
, в данном случае. Очевидно, Eclipse лучше справляется с этим из-за отсутствия сообщения об ошибке. К сожалению, на данный момент я не могу найти какие-либо отрывки JLS, описывающие такое поведение, поэтому я не могу точно сказать, является ли это ошибкой или просто Eclipse делает все возможное. - person awksp   schedule 18.07.2014test
), но, похоже, не может понять то же самое, удаляя еще один шаг, что кажется необычным, поскольку алгоритм, описанный JLS, кажется быть полностью рекурсивным, без реальных пределов максимальной сложности, о которых можно было бы говорить. В частности, компилятор определяет, чтоList<BoundedI2<?>>
можно присвоитьList<? extends Interface2<? extends Bound>>
якобы потому, чтоBoundedI2<?>
можно присвоитьInterface2<? extends Bound>
; почему бы и неInterface1<? extends Bound>
? - person user508633   schedule 20.07.2014Interface1<? extends Bound> i = (BoundedI2<?>) null;
, иInterface2<? extends Bound> i = (BoundedI2<?>) null;
компилируются без замечаний, но включение их в параметр типа дляList
заставляет компилятор сдаться. Я склоняюсь к тому, что это ошибка вjavac
. Вы можете попробовать отправить отчет об ошибке в Oracle, хотя кто знает, будет ли он устранен (не получил ответа на последний отчет, который я отправил, поэтому не слишком им доволен). Кроме того, вы можете отправить уведомление кому-либо, используя @‹username›. Не уверен, что вы мне отвечали, но на всякий случай. - person awksp   schedule 20.07.2014