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

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

简化后的代码如下:

JavaBugTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
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
1
2
3
public interface RetryCallback<T, E extends Throwable> {
T doWithRetry(Object var1) throws E;
}

用maven编译报错如下:

1
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修复的,所以可以查一下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配置好源码路径才能调试:

作者

Robert Lu

发布于

2020-03-06

许可协议

评论