LambdaConversionException при смешивании ссылки на метод и дженериков

Код ниже компилируется нормально, но выдает исключение во время выполнения. Это ожидаемое поведение и почему?

Код:

public static void main(String[] args) {
  A<Integer> a = new A<> ();
  System.out.println(a.min()); //prints null as expected
  System.out.println(a.max()); //throws exception
}

static class A<T extends Number & Comparable<? super T>> {
  Stream<T> s = Stream.empty();
  public T min() { return s.min((t1, t2) -> t1.compareTo(t2)).orElse(null); }
  public T max() { return s.max(T::compareTo).orElse(null); }
}

Выход:

null
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
    at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
    at abc$A.max(abc.java:19)
    at abc.main(abc.java:8)
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Number; not a subtype of implementation type interface java.lang.Comparable
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
    at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
    ... 4 more

person assylias    schedule 03.11.2015    source источник
comment
Я получаю ожидаемое java.lang.IllegalStateException: stream has already been operated upon or closed с java 1.8.0_60   -  person Alexis C.    schedule 03.11.2015
comment
@АлексисС. Странно (по крайней мере, ваш вывод имеет больше смысла, чем мой) - у меня тоже 1.8.0_60 - Windows x64.   -  person assylias    schedule 03.11.2015
comment
Что произойдет, если вы закомментируете первую строку (с минимальной), чтобы поток был открыт?   -  person Yosef Weiner    schedule 03.11.2015
comment
@SkinnyJ То же самое - я все еще получаю исключение   -  person assylias    schedule 03.11.2015
comment
Действительно, это странно, у меня OS X 10.11.1, если это поможет.   -  person Alexis C.    schedule 03.11.2015
comment
Я получаю то же поведение, что и @AlexisC. 1.8.0_60 Windows x64   -  person Yosef Weiner    schedule 03.11.2015
comment
Хм, JDK 1.8.0_60 Windows 10 x64, я получаю то же поведение, что и OP, то есть выбрасывается LambdaConversionException ... У меня есть IllegalStateException, если я изменяю аргумент max на (t1, t2) -> t1.compareTo(t2) вместо T::compareTo.   -  person Tunaki    schedule 03.11.2015
comment
Похоже, я получаю LambdaConversionException с ECJ. Вы уверены, что не используете это?   -  person Yosef Weiner    schedule 03.11.2015
comment
Это выглядит похоже, но я могу воспроизвести его даже с javac 1.8.0_60, который я не может сделать с примером в этом случае.   -  person Yosef Weiner    schedule 03.11.2015
comment
@assylias, что, если вы сделаете T только расширить Comparable (не Number)   -  person Yosef Weiner    schedule 03.11.2015
comment
Конечно, можно было бы просто использовать Comparator.naturalOrder() вместо T::compareTo, но жаль, что до сих пор есть проблемы с множественными границами…   -  person Holger    schedule 03.11.2015
comment
@SkinnyJ: я все еще могу воспроизвести проблему этого вопроса с помощью jdk1.8.0_60   -  person Holger    schedule 03.11.2015
comment
@Holger Должно быть, я был неясен. Согласитесь, я могу воспроизвести проблему этого вопроса с 1.8.0_60; но я не могу воспроизвести проблему ЭТОГО вопроса с этим компилятором (хотя могу с ECJ). Для меня указывает, что вопросы немного разные.   -  person Yosef Weiner    schedule 03.11.2015
comment
@SkinnyJ: Понятно. Что ж, есть по крайней мере что-то, что было исправлено в u45.   -  person Holger    schedule 03.11.2015
comment
Имейте в виду, что компилятор задействован. При использовании более старого компилятора, такого как встроенный в Netbeans, возникает проблема, даже если среда выполнения обновлена.   -  person Holger    schedule 04.11.2015
comment
Удаление @SkinnyJ Number решает проблему, но в любом случае, похоже, есть ошибка ...   -  person assylias    schedule 04.11.2015
comment
Судя по всему это баг   -  person Sleiman Jneidi    schedule 04.11.2015
comment
Я получаю LambdaConversionException с компилятором Eclipse Luna SR2 (4.4.2), но отлично работает с компилятором JDK 1.8.0_51 (Windows). Вы используете Эклипс? Какая версия. Пахнет жуком.   -  person clstrfsck    schedule 04.11.2015
comment
@assylias прав, но это указывает на выпуск нескольких облигаций (или что-то подобное). Можете ли вы подтвердить, компилируете ли вы с помощью eclipse или javac?   -  person Yosef Weiner    schedule 04.11.2015
comment
@SkinnyJ Похоже, это проблема Netbeans - при запуске того же из командной строки я получаю ожидаемый результат, но при запуске из Netbeans (который использует javac) я получаю вывод в своем вопросе.   -  person assylias    schedule 04.11.2015


Ответы (4)


Ваш код не будет работать, даже если вы используете лямбда-выражения вместо ссылок на методы, потому что поток уже исчерпан.

 System.out.println(a.min()); 
 System.out.println(a.max()); // exhausted

Потоки одноразовые. Но оставим это в стороне. Когда вы используете версию ссылки на метод, она захватывает Number в качестве параметра типа, а не Comparable, где Number не имеет compareTo может быть, потому что Number здесь более конкретен. Если вы просто используете Comparable, он будет работать нормально

  static class A<T extends Comparable<? super T>> {
    Stream<T> s = Stream.empty();
    public T min() { return s.min((t1, t2) -> t1.compareTo(t2)).orElse(null); }
    public T max() {
        T t = s.max(T::compareTo).orElse(null);
        return t; }
 }

System.out.println(a.max()); //null

ИМО (просто быть осторожным): я считаю, что это ошибка.

Во что я на самом деле верю: это определенно ошибка.

Редактировать: оказалось, что на самом деле это была ошибка, и она была исправлена, как подтвердил Брайан Гетц. https://bugs.openjdk.java.net/browse/JDK-8058112 . Согласно базе данных ошибок, это было исправлено в 8u40.

person Sleiman Jneidi    schedule 03.11.2015

Проблема инициализации сайта вызова решается с помощью JDK-8058112, доступного в JDK 8u40 b17 или потом.

person Srikanth    schedule 04.11.2015

Для тех, кто столкнулся с этой проблемой в 2017 году с java8 1.8.0_141, есть еще один зарегистрированный отчет об ошибке JDK. -8142476 и только версия исправления java9.

person hahn    schedule 25.07.2017

Кажется, это проблема Netbeans, и я не могу воспроизвести проблему при использовании javac из командной строки. Я подал отчет об ошибке.

person assylias    schedule 04.11.2015
comment
Я не думаю, что это ошибка Netbeans, проверьте мой ответ, это было сообщено как ошибка в компиляторе - person Sleiman Jneidi; 04.11.2015
comment
@SleimanJneidi Я не думаю, что это так. Эта ошибка была исправлена ​​в 8u40, и у меня есть проблема с 8u60 + код компилируется и работает нормально с javac (но не с Netbeans). - person assylias; 04.11.2015
comment
Если вы отключите компиляцию при сохранении, проблема исчезнет. Возможно, это отключит компилятор Netbeans и переключится на javac? - person Yosef Weiner; 04.11.2015