Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LambdaConversionException when mixing method reference and generics

The code below compiles fine but throws an exception at runtime. Is this the expected behaviour and why?

Code:

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); }
}

Output:

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
like image 914
assylias Avatar asked Nov 03 '15 19:11

assylias


4 Answers

Your code wouldn't work even if you use lambdas instead of method references because the stream is already exhausted

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

Streams are one off. But lets leave this apart. When you use the method reference version, it is capturing Number as a type parameter and not Comparable where Number has no compareTo maybe because Number is more specific here. If you just use Comparable it will work fine

  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

IMO (just to be cautious) : I believe it is a bug.

What I actually believe: It is definitely a bug.

Edit: It turns out that this was actually a bug and it was fixed as confirmed by Brian Goetz. https://bugs.openjdk.java.net/browse/JDK-8058112. According to the bug database, this was fixed in 8u40

like image 99
Sleiman Jneidi Avatar answered Nov 15 '22 18:11

Sleiman Jneidi


The call site initialization problem is addressed through JDK-8058112 available in JDK 8u40 b17 or later.

like image 28
Srikanth Avatar answered Nov 15 '22 18:11

Srikanth


For those who are facing this issue in 2017 with java8 1.8.0_141, there's another filed bug report JDK-8142476 and the fix version is java9 only.

like image 24
hahn Avatar answered Nov 15 '22 16:11

hahn


It seems to be a Netbeans issue and I can't reproduce the problem when using javac from the command line. I have filed a bug report.

like image 33
assylias Avatar answered Nov 15 '22 18:11

assylias