当前位置:  开发笔记 > 编程语言 > 正文

在Java 8中转换lambdas

如何解决《在Java8中转换lambdas》经验,为你挑选了2个好方法。

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上述类的方法中.迄今为止的实验产生了NoClassDefFoundVerifyError:

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 bug,但我很高兴错了!

这是一个说明行为的Github回购



1> Rafael Winte..:

对我来说,这似乎是JVM中的一个错误.系统类加载器尝试按名称定位转换后的类.但是,lambda表达式是通过匿名类加载加载的,其中包含以下条件:

clazz.getClassLoader()
     .loadClass(clazz.getName().substring(0, clazz.getName().indexOf('/')))

产生了ClassNotFoundException导致NoClassDefError.该类不被认为是真正的类,并且这样的匿名类例如不被传递到ClassFileTransformer转换的外部.

总而言之,在处理匿名类时,检测API对我来说有点麻烦.类似地,LambdaForms 传递给ClassFileTransformers但是带有所有参数但是classFileBuffer设置为null破坏变换器类的契约.

对于你的例子,问题似乎是你返回null; 当返回classFileBuffer什么是无操作时,问题就消失了.然而,这并不是ClassFileTransformer建议,返回null是建议的方式:

格式良好的类文件缓冲区(转换的结果),或者null如果不执行转换.

对我来说,这似乎是HotSpot中的一个错误.您应该将此问题报告给OpenJDK.

总而言之,正如我在我的代码操作库Byte Buddy中演示的那样,完全可以对匿名加载的类进行检测.与普通仪器相比,它需要一些不幸的调整,但运行时支持它.以下是成功在库中作为单元测试运行的示例:

Callable lambda = () -> "foo";

Instrumentation instrumentation = ByteBuddyAgent.install();
ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.of(instrumentation)
    .preregistered(lambda.getClass());
ClassFileLocator classFileLocator = ClassFileLocator.AgentBased.of(instrumentation, 
     lambda.getClass());

assertThat(lambda.call(), is("foo"));

new ByteBuddy()
  .redefine(lambda.getClass(), classFileLocator)
  .method(named("call"))
  .intercept(FixedValue.value("bar"))
  .make()
  .load(lambda.getClass().getClassLoader(), classReloadingStrategy);

assertThat(lambda.call(), is("bar"));



2> thetwan..:

Bug提交已被Oracle的人们接受,并被跟踪为JDK-8145964。这不是完全解决方案,但似乎是一个实际的运行时问题。

推荐阅读
360691894_8a5c48
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有