jdk 8u91一个lambda类型推断的bug

公司内部发布maven包是在公司构建服务器上编译的。最近一次上线,发现同样的代码,在本地的jdk8上能编译通过,但是在构建服务器上,就编译报错。

简化后的代码如下:

// JavaBugTest.java
import java.util.Optional;

public class JavaBugTest {

    public static <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E {
        return retryCallback.doWithRetry(new Object());
    }

    public static void main(String[] args) {
        Optional.ofNullable(execute((param) -> new Object()))
                .ifPresent(s -> System.out.println(s));
    }
}
// RetryCallback.java
public interface RetryCallback<T, E extends Throwable> {
    T doWithRetry(Object var1) throws E;
}

用maven编译报错如下:

JavaBugTest.java:[10,36] unreported exception E; must be caught or declared to be thrown

最后发现,是构建服务器jdk版本太老导致的。于是多试了几个版本,发现8u91不行,但是8u92就能编译通过了。

于是开始调试javac代码,发现两个版本的AST不一样:

8u91的AST
8u92的AST

可以看到,8u92以及之后的版本,作为Optional.ofNullable的参数,execute的异常能够被推断为RuntimeException,这一点是很正常的,因为我传进去的lambda本来就没有任何受检异常声明。

那么这个bug是怎么修复的呢?

可以查一下8u92的发布说明,里面提到了这个bug JDK-8066974 : Compiler doesn’t infer method’s generic type information in lambda body

修复代码如下:

简而言之,就是分析到ofNullable的时候,添加一个回调,等到内部的lambda类型推断完成后,再将这儿的类型修改掉(就是E改成了RuntimeException)。

P.S. JDK 8u91版本没有附带javac的源码,需要手动下载,并在IDEA配置好源码路径才能调试:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.