【发布时间】:2016-01-05 13:34:30
【问题描述】:
Java 8 似乎可以生成类来表示 lambda 表达式。比如代码:
Runnable r = app::doStuff;
大致表现为:
// $FF: synthetic class
final class App$$Lambda$1 implements Runnable {
private final App arg$1;
private App$$Lambda$1(App var1) {
this.arg$1 = var1;
}
private static Runnable get$Lambda(App var0) {
return new App$$Lambda$1(var0);
}
public void run() {
this.arg$1.doStuff();
}
}
据我了解,代码是在运行时生成的。现在,假设有人想将代码注入到上述类的run 方法中。迄今为止的实验产生了NoClassDefFound 和VerifyError 的混合:
java.lang.NoClassDefFoundError: App$$Lambda$2
at App$$Lambda$2/1329552164.run(Unknown Source)
at App.main(App.java:9)
Caused by: java.lang.ClassNotFoundException: App$$Lambda$2
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
这是针对:
$ java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
这甚至在将任何新字节码推入类之前。
这是预期的吗?闻起来像 JDK 错误,但我很高兴错了!
【问题讨论】:
-
验证错误表明您创建了损坏的字节码。您是否尝试调试代码?错误发生在什么操作期间?
-
我测试了一个 lambda 重新转换,它可以正常工作。你生成的代码一定有问题!
-
请记住,lambda 表达式和运行时类的映射是有意未指定的。多个 lambda 表达式可能共享一个类,或者同一表达式可能在运行时由不同的、不断变化的类表示。规范明确说明了这些可能性。因此,即使是 Instrumentation API 也得到了修复,以允许您对这样一个您如履薄冰的类进行检测。与特定 JVM 实现的一个特定版本一起工作的情况可能会在下一个版本中失败。
-
无论您想要实现什么,您最好通过检测创建
invokedynamic指令或目标方法来实现。不应有任何理由检测 lambda 表达式或方法引用的临时匿名类。 -
您不能假设这种行为在下一次重大更新之前保持不变。由于它是明确未指定的,它可能会在下一个小修订版中更改。这种内部结构的变化并不是第一次。例如,
7u6中的内部字符串表示发生了根本性的变化,8u20中添加了字符串重复数据删除功能……
标签: java java-8 bytecode instrumentation