Почему имеет значение, если я использую здесь ссылку на метод или лямбду? - PullRequest
6 голосов
/ 25 мая 2019

Когда я пытаюсь скомпилировать этот код

import java.util.Optional;

public class GenericTest {

    public static void main(String[] args) {
        Optional.empty().map(o -> getStringClass(o)).orElse(String.class);
    }

    static Class<?> getStringClass(Object arg) {
        return String.class;
    }

}

javac завершится ошибкой со следующей ошибкой:

GenericTest.java:6: error: method orElse in class Optional cannot be applied to given types;
                Optional.empty().map(o -> getStringClass(o)).orElse(String.class);
                                                            ^
  required: Class<CAP#1>
  found: Class<String>
  reason: argument mismatch; Class<String> cannot be converted to Class<CAP#1>
  where T is a type-variable:
    T extends Object declared in class Optional
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

Но если вместо этого я использую ссылку на метод, javac скомпилирует кодпросто отлично:

import java.util.Optional;

public class GenericTest {

    public static void main(String[] args) {
        Optional.empty().map(GenericTest::getStringClass).orElse(String.class);
    }

    static Class<?> getStringClass(Object arg) {
        return String.class;
    }

}

Почему это имеет значение, если я использую ссылку на метод или лямбда-выражение?

Насколько я понимаю, и ссылка на метод, и лямбда имеют типFunction<Object,Class<?>>, поэтому я не вижу здесь никакой разницы.
Java-компилятор eclipse (ecj), кстати, не скомпилирует обе версии.

Ответы [ 2 ]

4 голосов
/ 25 мая 2019

Это известное ограничение системы вывода типов компилятора, оно не работает с вызовами цепочечных методов, как в вашем первом фрагменте кода.

Возможные обходные пути?Используйте явно набранное лямбда-выражение:

Optional.empty()
        .map((Function<Object, Class<?>>) o -> getStringClass(o))
        .orElse(String.class);

или точную ссылку на метод (как вы уже пробовали):

Optional.empty().map(GenericTest::getStringClass).orElse(String.class);

или добавьте свидетеля типа:

Optional.empty().<Class<?>>map(o -> getStringClass(o)).orElse(String.class);

Похожие сообщения с похожей проблемой.

3 голосов
/ 25 мая 2019

Метод цепочки ударов снова.Вы можете прочитать здесь из , почему разработчикам этой функции было сложно реализовать (это связано с тем фактом, что ссылки на лямбда / метод являются поли-выражениями - их типы зависят от контекста).Такая функция потребует некоторой дополнительной нагрузки на компилятор - и хотя ваш пример будет довольно тривиальным примером для решения, javac должен заботиться не только о тривиальности;таким образом, это еще не реализовано, даже в Java-12.

Самое простое решение IMO, поскольку это связано с цепочкой методов (и в вашем случае это возможно), а не с цепочкой:

Optional<Class<?>> first = Optional.empty().map(o -> getStringClass(o));
Class<?> second = first.orElse(String.class);
...