Неоднозначный метод в Java 8, Почему? [дубликат]

этот вопрос уже есть ответ здесь:

public static void main(String... args){
    then(bar()); // Compilation Error
}

public static <E extends Exception> E bar() {
    return null;
}

public static void then(Throwable actual) { }

public static void then(CharSequence actual) { }

результат компиляции (из командной строки javac Ambiguous.java)

Ambiguous.java:4: error: reference to then is ambiguous
        then(bar());
        ^
  both method then(Throwable) in Ambiguous and method then(CharSequence) in Ambiguous match
1 error

почему этот метод неоднозначен? Этот код компилируется с успехом под Java 7!

после изменения панели методов на:

public static <E extends Float> E bar() {
    return null;
}

это компилируется без каких-либо проблем, но сообщается как ошибка в IntelliJ Idea (не может решить метод then(java.lang.FLoat)).

этот код терпит неудачу под Java 7 -javac -source 1.7 Ambiguous.java:

Ambiguous.java:4: error: no suitable method found for then(Float)
        then(bar());
        ^
    method Ambiguous.then(Throwable) is not applicable
      (argument mismatch; Float cannot be converted to Throwable)
    method Ambiguous.then(CharSequence) is not applicable
      (argument mismatch; Float cannot be converted to CharSequence)
1 error

версия Java

java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

1 ответов


рассмотрим следующий класс:

public class Foo extends Exception implements CharSequence {
    //...
}

класс Foo реализует как Throwable и CharSequence. Так в случае E установлен в этот экземпляр, компилятор Java не знает, какой метод вызвать.

причина, вероятно, нет никаких проблем для Java7 это то, что дженерики менее реализованы. В случае, если вы не предоставите E себя (например,(Foo) bar()), Java вернется к базовой версии E что это implements Exception, E таким образом, считается только экземпляром Exception.

на Java8, the вывод типа улучшена, типа E теперь является производным от параметра, вызываемого then(), другими словами компилятор сначала смотрит, какие возможные типы then() потребностей, проблема заключается в том, что они оба являются допустимыми. Поэтому в этом случае она становится двусмысленной.


подтверждение концепция:

теперь мы немного изменим ваш код и покажем, как разрешаются неоднозначные вызовы:

скажем, мы изменим код на:

public class Main {
    public static void main(String... args){
        then(bar()); // Compilation Error
    }
    public static <E extends Exception> E bar() {
        return null;
    }
    public static void then(CharSequence actual) {
        System.out.println("char");
    }
}

если вы запустите это в Java8, нет проблем (он печатает char), потому что Java8 просто предполагает, что есть такой класс Foo (Он создал для него какой-то "внутренний" тип, производный от обоих).

работает в Java7 дает проблемы:

/MyClass.java:18: error: method then in class MyClass cannot be applied to given types;
    then(bar()); // Compilation Error
    ^
  required: CharSequence
  found: Exception
  reason: actual argument Exception cannot be converted to CharSequence by method invocation conversion
1 error

он сделал запасной вариант на Exception и не смог найти тип, который мог бы справиться с этим.

если вы запустите код в Java8, это ошибка из-за неоднозначного вызова, если вы запустите его в Java7 однако он будет использовать Throwable метод.


короче: компилятор стремится "угадать", что E находится в Java8, тогда как в Java7 самый консервативный тип был выбранный.