Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does javac insert Objects.requireNonNull(this) for final fields?

Consider the following class:

class Temp {     private final int field = 5;      int sum() {         return 1 + this.field;     } } 

Then I compile and decompile the class:

> javac --version javac 11.0.5  > javac Temp.java  > javap -v Temp.class   ...   int sum();     descriptor: ()I     flags: (0x0000)     Code:       stack=2, locals=1, args_size=1          0: iconst_1          1: aload_0          2: invokestatic  #3   // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;          5: pop          6: iconst_5          7: iadd          8: ireturn 

In simple words, javac compiles sum() to this:

int sum() {     final int n = 1;     Objects.requireNonNull(this); // <---     return n + 5; } 

What is Objects.requireNonNull(this) doing here? What's the point? Is this somehow connected to reachability?

The Java 8 compiler is similar. It inserts this.getClass() instead of Objects.requireNonNull(this):

int sum() {     final int n = 1;     this.getClass(); // <---     return n + 5; } 

I also tried to compile it with Eclipse. It doesn't insert requireNonNull:

int sum() {     return 1 + 5; } 

So this is javac-specific behavior.

like image 296
ZhekaKozlov Avatar asked Jun 12 '20 08:06

ZhekaKozlov


People also ask

What's the point of Objects requireNonNull?

requireNonNull. Checks that the specified object reference is not null and throws a customized NullPointerException if it is. Unlike the method requireNonNull(Object, String) , this method allows creation of the message to be deferred until after the null check is made.

What is Objects nonNull in Java?

The nonNull method is a static method of the Objects class in Java that checks whether the input object reference supplied to it is non-null or not. If the passed object is non-null, then the method returns true. If the passed object is null , then the method returns false.


1 Answers

Since the field is not only final, but a compile-time constant, it will not get accessed when being read, but the read gets replaced by the constant value itself, the iconst_5 instruction in your case.

But the behavior of throwing a NullPointerException when dereferencing null, which would be implied when using a getfield instruction, must be retained¹. So when you change the method to

int sumA() {   Temp t = this;   return 1 + t.field; } 

Eclipse will insert an explicit null check too.

So what we see here, is javac failing to recognize that in this specific case, when the reference is this, the non-null property is guaranteed by the JVM and hence, the explicit null check is not necessary.

¹ see JLS §15.11.1. Field Access Using a Primary:

  • If the field is not static:

    • The Primary expression is evaluated. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.
    • If the value of the Primary is null, then a NullPointerException is thrown.
    • If the field is a non-blank final, then the result is the value of the named member field in type T found in the object referenced by the value of the Primary.

like image 143
Holger Avatar answered Oct 25 '22 19:10

Holger