简体   繁体   中英

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

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

通过JDK 8u40 b17或更高版本中提供的JDK-8058112解决了呼叫站点初始化问题。

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 .

对于那些在2017年使用java8 1.8.0_141面临此问题的java8 1.8.0_141 ,还有另一个提交的错误报告JDK-8142476 ,修复版本java9

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM