首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >您能在运行时检查Java 8 lambda的字节代码吗?

您能在运行时检查Java 8 lambda的字节代码吗?
EN

Stack Overflow用户
提问于 2014-07-04 18:21:26
回答 4查看 2.7K关注 0票数 23

如果您有一个匿名类,比如

代码语言:javascript
复制
Predicate<String> isEmpty = new Predicate<String>() {
    public boolean test(String t) {
        return t.isEmpty();
    }
};

传递给isEmpty引用的库可以检查字节代码,以查看它所做的事情,并可能对其进行操作。你能为兰巴达做这件事吗?

代码语言:javascript
复制
Predicate<String> isEmpty = String::isEmpty;

例如,有这个代码和字节码

代码语言:javascript
复制
public class Main {
    public static void test(Predicate<String> tester) {
        System.out.println("tester.getClass()= " + tester.getClass());
        System.out.println("tester.getClass().getClassLoader()="+ tester.getClass().getClassLoader());
    }
    public static void main(String... args) {
        Predicate<String> isEmpty = String::isEmpty;
        test(isEmpty);
    }
}

$ javap -cp . -c -private Main.class
Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void test(java.util.function.Predicate<java.lang.String>);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: new           #3                  // class java/lang/StringBuilder
       6: dup           
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: ldc           #5                  // String tester.getClass()= 
      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_0       
      16: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      28: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: new           #3                  // class java/lang/StringBuilder
      34: dup           
      35: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      38: ldc           #11                 // String tester.getClass().getClassLoader()=
      40: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: aload_0       
      44: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      47: invokevirtual #12                 // Method java/lang/Class.getClassLoader:()Ljava/lang/ClassLoader;
      50: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      53: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      56: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      59: return        

  public static void main(java.lang.String...);
    Code:
       0: invokedynamic #13,  0             // InvokeDynamic #0:test:()Ljava/util/function/Predicate;
       5: astore_1      
       6: aload_1       
       7: invokestatic  #14                 // Method test:(Ljava/util/function/Predicate;)V
      10: return        
}

对于testertest中的引用,如何找到调用哪个方法?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-07-06 17:38:21

如果您只想看到字节码:

代码语言:javascript
复制
javap -c -p -v classfile
      ^disassemble
         ^private methods
            ^verbose, including constant pool and bootstrap methods attribute

但是,如果您想在运行时尝试这样做,那么您几乎是运气不佳(从设计上讲,我们没有任何类似表达式树的东西),正如另一个答案所示。

票数 12
EN

Stack Overflow用户

发布于 2014-07-05 02:24:44

简单的答案是:你不能。关于这件事有一个Brian Goetz的相关回答。为了实现lambda表达式,javac创建了一个INVOKEDYNAMIC指令,它将调用委托给LambdaMetafactory的引导方法,对于OpenJDK,这个引导方法是在运行时使用ASM创建所需接口的实现。

test方法中,退出的实例tester属于这个ASM生成的类,因此您没有类文件可用于查找Predicate表示的方法。在一般情况下,如何将lambda表达式映射到接口实现的确切决定由运行时环境来决定,这将使您的问题更加困难。了解lambda表达式表示哪个方法的唯一方法是读取其创建的字节代码,即解释示例中的main方法。

票数 7
EN

Stack Overflow用户

发布于 2014-10-06 13:16:07

毕竟,我找到了一种获得lambda表达式的类文件的方法,它相当可靠(但当然仍然依赖于实现细节)。对于一个实验性的实现,我现在使用的是一个Java代理,它允许类的重新转换,但它本身是作为一个非op实现的,我只对作为参数传递的二进制数组感兴趣。

在通过注册的Instrumentation代理获得一个ClassFileTransformer实例之后,一个注册一个ClassFileTransformer,然后在重新转换lambdaInstance.getClass()之后,通过二进制表示通知它。当然,这是一个相当麻烦的解决方案,将来一旦lambdas的内部实现发生变化,它可能会中断。

不幸的是,我没有找到任何关于代理应该如何在标准兼容JVM上对lambda表达式的合成类进行类重新转换的文档。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24579276

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档