当前位置: 首页 > 面试题库 >

在Java Lambda中,为什么在捕获的变量上调用getClass()

朱啸
2023-03-14
问题内容

如果您查看字节码

Consumer<String> println = System.out::println;

Java 8更新121生成的字节码是

GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
DUP
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
  // handle kind 0x6 : INVOKESTATIC
  java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  // arguments:
  (Ljava/lang/Object;)V, 
  // handle kind 0x5 : INVOKEVIRTUAL
  java/io/PrintStream.println(Ljava/lang/String;)V, 
  (Ljava/lang/String;)V
]
ASTORE 1

getClass()方法被调用System.out,结果被忽略。

这是间接的空引用检查吗?

当然,如果您跑步

PrintStream out = null;
Consumer<String> println = out::println;

这将触发NullPointerException。


问题答案:

是的,调用getClass()已成为规范的“测试null”成语,正如getClass()预期的那样,这是一种廉价的内在操作,而且我想null,如果getClass()未使用。

另一个示例是使用不是的外部实例创建内部类实例this

public class ImplicitNullChecks {
    class Inner {}
    void createInner(ImplicitNullChecks obj) {
        obj.new Inner();
    }

    void lambda(Object o) {
        Supplier<String> s=o::toString;
    }
}

编译为

Compiled from "ImplicitNullChecks.java"
public class bytecodetests.ImplicitNullChecks {
  public bytecodetests.ImplicitNullChecks();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  void createInner(bytecodetests.ImplicitNullChecks);
    Code:
       0: new           #23                 // class bytecodetests/ImplicitNullChecks$Inner
       3: dup
       4: aload_1
       5: dup
       6: invokevirtual #24                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
       9: pop
      10: invokespecial #25                 // Method bytecodetests/ImplicitNullChecks$Inner."<init>":(Lbytecodetests/ImplicitNullChecks;)V
      13: pop
      14: return

  void lambda(java.lang.Object);
    Code:
       0: aload_1
       1: dup
       2: invokevirtual #24                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
       5: pop
       6: invokedynamic #26,  0             // InvokeDynamic #0:get:(Ljava/lang/Object;)Ljava/util/function/Supplier;
      11: astore_2
      12: return
}

另请参阅JDK-8073550:

我们类库中的一些地方使用了使用object.getClass()来检查无效性的怪异技巧。尽管这看起来很明智,但实际上使人们误以为这是已批准的空值检查做法。

使用JDK 7,我们有Objects.requireNonNull提供正确的null检查,并正确声明意图。

这是否也应适用于编程语言固有检查可能是值得商,的,因为Objects.requireNonNull为此目的使用该方法将对java.lang包外部的类创建依赖关系,而该类在源代码中不可见。在这种特定情况下,该技巧仅对那些查看字节码的人可见。但是已经决定更改Java
9的行为。

这是jdk1.9.0b160编译相同测试类的方式:

Compiled from "ImplicitNullChecks.java"
public class bytecodetests.ImplicitNullChecks {
  public bytecodetests.ImplicitNullChecks();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  void createInner(bytecodetests.ImplicitNullChecks);
    Code:
       0: new           #26                 // class bytecodetests/ImplicitNullChecks$Inner
       3: dup
       4: aload_1
       5: dup
       6: invokestatic  #27                 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
       9: pop
      10: invokespecial #28                 // Method bytecodetests/ImplicitNullChecks$Inner."<init>":(Lbytecodetests/ImplicitNullChecks;)V
      13: pop
      14: return

  void lambda(java.lang.Object);
    Code:
       0: aload_1
       1: dup
       2: invokestatic  #27                 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
       5: pop
       6: invokedynamic #29,  0             // InvokeDynamic #0:get:(Ljava/lang/Object;)Ljava/util/function/Supplier;
      11: astore_2
      12: return
}


 类似资料:
  • 问题内容: 本地类的Java文档说: 此外,局部类可以访问局部变量。但是,局部类只能访问声明为final的局部变量。当局部类访问封闭块的局部变量或参数时,它将捕获该变量或参数。例如,PhoneNumber构造函数可以访问局部变量numberLength,因为它被声明为final。numberLength是捕获的变量。 捕获的变量是什么,其用途是什么,为什么需要它?请帮助我理解它的概念。 问题答案:

  • 大概是这样的: 谢谢[已解决] 也感谢Umer Farooq的回答:从lambda内部修改局部变量

  • 如问题所示,值可变的lambda捕获不适用于常量 但是为什么对不可变的lambda也这样做呢?在不可变lambda中,声明为,因此它无论如何都不能修改捕获的值。 当我们移动lambda时,这种情况的坏后果就会发生,例如,当我们将lambda包装在一个中时。 请参见以下两个示例: 我们得到以下编译器生成的lambda类: 程序打印以下内容(使用gcc或clang): 在第一个示例中,至少复制了三次值

  • 问题内容: 在有效的Java(第275页)中,有以下代码段: 捕获中断异常以重新引发它有什么用?为什么不让它飞呢? 问题答案: 简单的答案是,这是一个检查的异常,它不在方法(或方法)的签名中。所以你必须抓住它。一旦发现它,建议您设置为设置中断标志。除非您确实打算压缩中断。

  • 问题内容: 我试图理解Java中捕获变量的概念。 我发现了有关它的详细文章:http : //www.devcodenote.com/2015/04/variable-capture-in- java.html 我不确定字节码部分: 类似地,为了访问封闭方法的局部变量,将创建变量的隐藏副本,并将其保留在内部类 文件 中,该 文件 从该变量访问该变量。 当最终原始值在编译时未知时,如何将其保存到类文

  • 例如: 但是你可以在块之前声明它,然后它就可以正常工作了: 我只是想知道这样做的设计原因。为什么在块中创建的对象不在方法其余部分的范围内?也许我没有深入了解除了只是观察抛出的之外是如何工作的。