Ответ 1
Интуитивно понятный способ проверки того, является ли method1
более конкретным, чем method2
, заключается в том, чтобы увидеть, можно ли реализовать method1
путем вызова method2
с теми же параметрами
method1(params1){
method2(params1); // if compiles, method1 is more specific than method2
}
Если существуют varargs, нам может понадобиться развернуть vararg, чтобы 2 метода имели одинаковое количество параметров.
Проверьте первые два method()
в вашем примере
<K> void method_a(K arg, Object... otherArgs) {
method_b(arg, otherArgs); //ok L1
}
<K> void method_b(Object arg, Object... otherArgs) { // extract 1 arg from vararg
method_a(arg, otherArgs); //ok L2
}
(типы возврата не используются при определении специфичности, поэтому они опущены)
Оба компилируются, поэтому каждый из них более конкретный, чем другой, следовательно, двусмысленность. То же самое касается ваших method2()
s, они более специфичны друг для друга. Поэтому вызов method2()
неоднозначен и не должен компилироваться; в противном случае это ошибка компилятора.
Итак, что говорит спецификация; но это правильно? Конечно, method_a
выглядит более конкретно, чем method_b
. На самом деле, если у нас есть конкретный тип вместо K
void method_a(Integer arg, Object... otherArgs) {
method_b(arg, otherArgs); // ok
}
void method_b(Object arg, Object... otherArgs) {
method_a(arg, otherArgs); // error
}
то только method_a
более специфичен, чем method_b
, а не наоборот.
Расхождение возникает из магии вывода типа. L1
/L2
вызывает общий метод без явных аргументов типа, поэтому компилятор пытается вывести аргументы типа. Цель алгоритма определения типа - найти аргументы типа, которые компилируют код! Неудивительно, что L1 и L2 компилируются. L2 на самом деле состоит в том, чтобы быть this.<Object>method_a(arg, otherArgs)
Ввод типа пытается угадать, чего хочет программист, но предположение должно быть неправильным иногда. Наше реальное намерение на самом деле
<K> void method_a(K arg, Object... otherArgs) {
this.<K>method_b(arg, otherArgs); // ok
}
<K> void method_b(Object arg, Object... otherArgs) {
this.<K>method_a(arg, otherArgs); // error
}