Java 8: Ссылка на [метод] неоднозначна
Кто-нибудь понимает, почему следующий код будет компилироваться в Java 7 и ниже, но не работает с Java 8.
public static void main(String[] args) throws Exception {
put(get("hello"));
}
public static <R> R get(String d) {
return (R)d;
}
public static void put(Object o) {
System.err.println("Object " + o);
}
public static void put(CharSequence c) {
System.err.println("CharSequence " + c);
}
public static void put(char[] c) {
System.err.println("char[] " + c);
}
Метод get имеет общий тип возвращаемого значения. В JDK 7 и ниже этот компилируется, и выбран метод put с параметром Object. В JDK 8 это невозможно скомпилировать, указывая, что метод put неоднозначен.
По-видимому, JDK 8 пропускает метод Object-parameter и находит последние два метода объекта-объекта и жалуется на них (т.е. если вы добавите другой метод put с некоторым другим типом параметра, компилятор переключится и начнет жаловаться новые последние два метода)
Это похоже на ошибку.
Ответы
Ответ 1
Ваша проблема заключается в побочном эффекте Обобщенный вывод целевого типа, улучшение в Java 8.
Что такое вывод целевого типа
Возьмем ваш примерный метод,
public static <R> R get(String d) {
return (R)d;
}
Теперь, в вышеприведенном методе, общий параметр R
не может быть разрешен компилятором, потому что нет параметра с R
.
Итак, они ввели понятие под названием Target-type Inference
, которое позволяет параметру выводить на основе параметра назначения.
Итак, если вы это сделаете,
String str = get("something"); // R is inferred as String here
Number num = get("something"); // R is inferred as Number here
Это хорошо работает в Java 7. Но следующий не,
put(get("something");
static void Put(String str) {} //put method
Потому что вывод типа работал только для прямых назначений.
Если нет прямого назначения, то общий тип был выведен как Object
.
Итак, когда вы скомпилировали код с Java 7, ваш метод put(Object)
был вызван без каких-либо проблем.
Что они сделали в Java 8
Они улучшили вывод типа, чтобы вывести тип из вызовов методов и вызовов с цепочкой
Подробнее о них здесь и здесь
Итак, теперь вы можете напрямую вызвать put(get("something"))
, а общий тип будет выводится на основе параметра метода put()
.
Но, как вы знаете, методы, put(Charsequence)
и put(char[])
соответствуют аргументам. Так что там двусмысленность.
Фикс?
Просто сообщите компилятору, что вы хотите,
put(TestClass.<CharSequence>get("hello")); // This will call the put(CharSequence) method.
Ответ 2
Похоже, что это известная несовместимость.
См. раздел "Область: Инструменты /javac " в этой статье.
И эта ошибка.
Сводка
Следующий код, скомпилированный с предупреждениями в JDK 7, не будет компилироваться в JDK 8:
import java.util.List;
class SampleClass {
static class Baz<T> {
public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
return null;
}
}
private static void bar(Baz arg) {
Baz element = Baz.sampleMethod(arg).get(0);
}
}
Компиляция этого кода в JDK 8 вызывает следующую ошибку:
SampleClass.java:12: error:incompatible types: Object cannot be converted to Baz
Baz element = Baz.sampleMethod(arg).get(0);
Note: SampleClass.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
В этом примере исходный тип передается в sampleMethod (Baz), который применяется подтипированием (см. JLS, Java SE 7 Edition, раздел 15.12.2.2).
Непроверенный необходимо, чтобы метод был применим, поэтому его возвращение тип удаляется (см. JLS, Java SE 7 Edition, раздел 15.12.2.6). В в этом случае возвращаемый тип sampleMethod (Baz) java.util.List вместо java.util.List > и, следовательно, return type get (int) - это Object, который не совместим с назначением с Базом.