本文介绍了在Java 8中转换lambdas的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java 8似乎生成了表示lambda表达式的类。例如,代码:

Java 8 appears to generate classes to represent lambda expressions. For instance, the code:

  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 的混合:

As I understand this, the code is generated at runtime. Now, suppose one wanted to inject code into the run method of the above class. Experiments thus far yield a mix of NoClassDefFound and 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)

甚至在将任何新字节码推入课程之前。

This is even before pushing any new bytecode into the class.

这是前者吗pected?闻起来就像一个JDK错误,但我很高兴错了!

Is this expected? Smells like a JDK bug, but I'm happy to be wrong!

这是一个

推荐答案

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

To me, this seems like a bug in the JVM. The system class loader attempts to locate the transformed class by its name. However, lambda expressions are loaded via anonymous class loading where the following condition:

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

产生 ClassNotFoundException ,导致 NoClassDefError 。该类不被视为真正的类,并且这样的anonyoumous类例如不会在重新转换之外传递给 ClassFileTransformer

yields a ClassNotFoundException resulting in the NoClassDefError. The class is not considered a real class and such anonyoumous classes are for example not passed to a ClassFileTransformer outside of a retransform.

总而言之,在处理匿名类时,检测API对我来说有点麻烦。同样, LambdaForm s 传递给 ClassFileTransformer 但是所有参数都是 classFileBuffer 设置为 null 什么打破了变压器类的合约。

All in all, the instrumentation API feels a bit buggy to me when dealing with anonymous classes. Similarly, LambdaForms are passed to ClassFileTransformers but with all arguments but the classFileBuffer set to null what breaks the transformer class's contract.

对于您的示例,问题似乎是您返回 null ;返回 classFileBuffer 什么是无操作时,问题就消失了。然而,这不是 ClassFileTransformer 建议的,其中返回 null 是建议的方法:

For your example, the problem seems to be that you return null; the problem goes away when returning the classFileBuffer what is a no-op. This is however not what the ClassFileTransformer suggests, where returning null is the recommended way of doing this:

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

To me, this seems like a bug in HotSpot. You should report this issue to the OpenJDK.

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

All in all, it is perfectly possible to instrument anonymously loaded classes as I demonstrate in my code manipulation library Byte Buddy. It requires some unfortunate tweaks compared to normal instrumentation but the runtime supports it. Here is an example that successfully runs as a unit test within the library:

Callable<String> 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"));

这篇关于在Java 8中转换lambdas的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 13:05