Подтвердить что ты не робот

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 с некоторым другим типом параметра, компилятор переключится и начнет жаловаться новые последние два метода)

Это похоже на ошибку.

4b9b3361

Ответ 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, который не совместим с назначением с Базом.