记一次遇到Optional的ifpersent的空指针异常

在使用Optional时发现一个场景如下代码所示:(简化后的代码)

 public static class Content {
        private String a;

        public void setA(String a) {
            this.a = a;
        }
    }

    public static void main(String[] args) {
        Content c = null;
        String str = null;
        Optional.ofNullable(str).ifPresent(c::setA);
    }

这段代码本意是要在str为空的时候不要调用c.setA()方法,那么此时即使c为null也不会产生异常。但是在实际运行过程中却发现会抛出NullPointerException异常。

Exception in thread "main" java.lang.NullPointerException
    at com.test.Test.main(Test.java:18)

代码已经简化的很简单了基本找不到什么问题,初步猜测应该是Java语法糖在编译成字节码的过程中加入了一些其他指令。接下来使用javap命令反解析class文件得到最终生成的指令内容,内容如下:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: aconst_null
         1: astore_1
         2: aconst_null
         3: astore_2
         4: aload_2
         5: invokestatic  #21                 // Method java/util/Optional.ofNullable:(Ljava/lang/Object;)Ljava/util/Optional;
         8: aload_1
         9: dup
        10: invokevirtual #22                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
        13: pop
        14: invokedynamic #23,  0             // InvokeDynamic #0:accept:(Lcom/test/Test$Content;)Ljava/util/function/Consumer;
        19: invokevirtual #24                 // Method java/util/Optional.ifPresent:(Ljava/util/function/Consumer;)V
        22: return
      LineNumberTable:
        line 71: 0
        line 72: 2
        line 73: 4
        line 74: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  args   [Ljava/lang/String;
            2      21     1     c   Lcom/test/Test$Content;
            4      19     2   str   Ljava/lang/String;

果不其然在10:行执行Optional.ofNullable()方法后,ifPresent之前,对变量c执行了一个c.getClass()的方法调用。由于在此处c为null,所以抛出了空指针异常。程序报错的原因就在这里,那么这里为什么要执行getClass操作呢?
有意思的是小伙伴在Java11执行的结果是:

Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
        at com.test.Test.main(Test.java:18)

解析字节码得到的内容发现10:行由
【invokevirtual #22 // Method java/lang/Object.getClass:()Ljava/lang/Class;】
变成了
【 invokestatic #27 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;】,
那么也就是说这里可能是要进行一个非空校验。
在openjdk的记录里变更原因也证实了这个猜想
【https://bugs.openjdk.java.net/browse/JDK-8073479#】

所以在使用方法引用的时候一定要注意这个问题,在代码没有执行到的情况下也会抛出空指针异常呦

你可能感兴趣的:(记一次遇到Optional的ifpersent的空指针异常)