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

2023-11-26

如果你看一下字节码

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

Java 8 update 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

The getClass()方法正在被调用System.out并且结果被忽略。

这是间接空引用检查吗?

当然如果你跑

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

这会触发 NullPointerException。


是的,正在打电话getClass()已成为规范的“测试null” 成语,如getClass()预计是一个廉价的内在操作,我想,HotSpot 可能能够检测到这种模式并将操作简化为内在操作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
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Java Lambda 中,为什么对捕获的变量调用 getClass() 的相关文章

随机推荐

  • 使用curl和php从url读取xml数据

    我想从 URL 读取 XML 数据 我的网址如下 这是我的代码 Url http www arrowcast net fids mco fids asp sort city city number airline adi A if func
  • jQuery 选择器和反斜杠

    我有一个 dom 元素 其中包含完全限定名称作为 id 属性的一部分 div My Div div 让 jQuery 通过 ID 选择元素似乎是不可能的 这是我的实验 var e1 domain element div var e2 dom
  • 在数据库 mysql 中存储和检索同义词的最佳方法

    我正在制作一个同义词列表 我将其存储在数据库中并在进行全文搜索之前检索它 当用户输入 word1 我需要在同义词表中查找这个词 因此 如果找到该单词 我将选择该单词的所有同义词 并在下一个查询的全文搜索中使用它 我将在其中构造查询 例如 M
  • ggplot:仅在满足某些条件时才绘制图层

    有没有过滤的方法within ggplot本身 也就是说 说我想这样做 p lt ggplot iris aes x Sepal Width y Sepal Length species geom point size 4 shape 4
  • 扩展 Scala 集合

    我想要一个在尝试覆盖现有键的值时抛出的映射 我试过 trait Unoverwriteable A B extends scala collection Map A B case class KeyAlreadyExistsExceptio
  • 如何在 yacc 中将 yylval 与字符串一起使用

    我想传递令牌的实际字符串 如果我有一个名为 ID 的令牌 那么我希望我的 yacc 文件真正知道 ID 的名称 我想我必须使用 yylval 将字符串从 Flex 文件传递 到 yacc 文件 我怎么做 通过 yylval 返回字符串或任何
  • Castle DynamicProxy:代理接口时如何代理等于?

    我需要使用 Castle DynamicProxy 通过向 ProxyGenerator CreateInterfaceProxyWithTarget 提供接口实例来代理接口 我还需要确保对 Equals GetHashCode 和 ToS
  • 测试所有图片是否加载完毕

    这是我尝试测试所有图像是否已加载的能力 for var i 0 i lt imgCount i loadArr i false imgArr i new Image imgArr i src img i png imgArr i onloa
  • 为什么 pip 安装我的软件包的旧版本?

    我刚刚将我的包的新版本上传到 PyPi 1 2 1 0 r4 我可以下载 Egg 文件并使用 easy install 安装它 并且版本检查正确 但是当我尝试使用 pip 安装时 它会安装版本 1 1 0 0 即使我明确指定 pip 的版本
  • R:使用前一行的值更新(视条件而定)

    我想用组内前一行的值更新表中的值 并且可能在给定条件下停止更新 这是一个例子 set seed 12345 field lt data table time 1 3 player letters 1 2 prospects round rn
  • 纹理视图获取表面

    我正在使用 ExoPlayer 库 它需要一个 Surface 但是我找不到任何方法来检索 Textureview 的底层 Surface 有任何想法吗 Surfaceview 有一个方法 surfaceView getHolder get
  • 可以物理访问源生成器创建的文件吗?

    是否有任何标准方法 选项 如何排列通过生成的文件Source Generators并添加到构建过程中也在项目结构中物理可见 因此开发人员可以查看结果代码 例如通过 VS 解决方案资源管理器窗口 我的意思是除了将代码直接保存到生成器的文件中之
  • Spring Data - 如果参数具有空值,则忽略该参数

    我想要一个带有两个参数的 Spring 数据存储库接口 有没有办法让它具有以下行为 MyObject findByParameterOneAndParameterTwo String parameterOne String paramete
  • Javac 缺少有效最终优化

    Fact javac被编程来检测变量是否final或者如果它可以被视为有效地 final Proof 这段代码说明了这一点 public static void finalCheck String str1 hello Runnable r
  • WiX RemoveFolderEx 不起作用?

    我希望 WiX 在卸载时删除 AppData 文件夹 因此我查看了 RemoveFolderEx 并遵循了一些有关如何使其工作的帖子 指南 据我所知 我的实现应该有效 但事实并非如此 我正在使用以下内容
  • 如何一次构建多个包二进制文件

    我在不同的地方看到过这个讨论 答案包括 使用 cmd foo cmd bar 类型的文件夹结构 这对我不起作用 这有效 du a 8 src cmd bin1 main go 8 src cmd bin1 8 src cmd bin2 ma
  • ESLint 与 React 给出 `no-unused-vars` 错误

    我已经设置了eslint eslint plugin react 当我运行 ESLint 时 linter 返回no unused vars每个 React 组件的错误 我假设它没有识别出我正在使用 JSX 或 React 语法 有任何想法
  • android 显示软键盘时如何向上移动布局

    我的登录屏幕有两个EditTexts和我的布局中的登录按钮 问题是 当我开始打字时 会显示软键盘并覆盖登录按钮 当布局出现时 如何将布局向上或键盘上方推 我不想使用ScrollView 只想实现它而不向下滚动 那怎么办呢 Set windo
  • 防止服务器中出现多个实例相同的用户名

    我开发了一个托管在一台服务器上的应用程序 许多用户通过远程桌面连接访问它 但有时我在任务管理器中看到同一用户打开了 2 个实例 我需要防止同一用户无法打开多个实例 但请注意 该程序可以由不同的用户多次打开 请原谅我的英语 谢谢 PS 我使用
  • 在 Java Lambda 中,为什么对捕获的变量调用 getClass()

    如果你看一下字节码 Consumer