Recently I found a very strange NullPointerException. I didn't even understand it at first when this method is thrown below. Even if dSrc is null, it's okay to assign a value directly to distinct.
private Doubledistinct; private void setParam(Double dSrc, boolean flag) { this.distinct = (flag) ? dSrc : 0d; }
Finally, we found that it was a hidden rule for Java automatic box unpacking. Let's take a look at it.
Automatic packing/unpacking
The introduction of automatic packing/unpacking in JDK1.5 improves our development efficiency and makes our code more concise without explicit conversion:
Double dWrap1 = 10d; double d1 = dWrap1; double d2 = d1 + dWrap1; DoubledWarp2 = d2 + dWrap1;
In fact, automatic packing/unpacking is supported by the compiler, And the JVM has not changed. We can see that the above source code will become:
Double dWrap1 = Double.valueOf(10.0d); double d1 = dWrap1.doubleValue(); double d2 = d1 + dWrap1.doubleValue(); DoubledWarp2 = Double.valueOf(d2 + dWrap1.doubleValue());
The intention of the compiler is obvious, which helps us to convert the basic types and encapsulation types. In addition, for the operation of encapsulation classes, we need to convert them to the basic types before computation.
However, this automatic conversion problem arises. If I initialize dWrap1 to null and assign it to d1, it is equivalent to assigning null to the basic type double. There is no problem during compilation, because the compiler also thinks it is a Double encapsulation class, which will automatically assign a value to d1 for unpacking, but it will only be thrown during runtime.NullPointerException, As follows:
Double dWarp1 = null; double d1 = dWarp1;
This is actually a very low-level bug, which is easy to prevent and can be avoided by adding a non-empty verification. The general principle is that non-empty verification is required before each Object is used. Some code check tools will also help us with this verification, such as FindBugs. Therefore, we can write the following form:
Double dWarp1 = null; double d1 = 0d; if (null != dWarp1) { d1 = dWarp1; }
Potential rules for three-object operations
Sometimes, to simplify the code, we will introduce the Three-object OPERATOR:
double d1 = (null != dWarp1) ? dWarp1 : 0d;
However, there are also some strange situations: Based on the flag condition, if true, the value is dWarp1; otherwise, the default value is 0, as shown below.
Double dWarp1 = null; boolean flag =true; DoubledWarp2 = (flag) ? dWarp1 : 0d;
At first glance, it is normal. It is equivalent to dWarp2 = dWarp1, but an exception is thrown during running.NullPointerException.
This is a problem caused by the compiler's automatic packing/unpacking conversion. We can see from the decompilation that it had previously split the dWarp1 layer, so that the problem we mentioned above occurs. If dWarp1 is null, it will go down.
DoubledWarp2 = Double.valueOf((flag) ? dWarp1.doubleValue() : 0.0D);
In fact, this is the feature of automatic packing/unpacking. As long as an operation has different types and involves type conversion, the compiler will go down (Basic Type), And then perform operations. That is to say, if the operations include int and Integer, the Integer is converted to int before calculation.
Therefore, the NullPointerException will still be thrown in the following method:
Double dWarp1 = null; Long l2 = null; boolean flag =false; DoubledWarp2 = (flag) ? dWarp1 : l2;
Because both dWarp1 and l2 must first be converted to the basic type, instead of mutual conversion. After decompilation, they will become:
Double dWarp2 = Double.valueOf((flag)? dWarp1.doubleValue() : l2.longValue());
Therefore, the following three methods can be changed:
1. Before assigning values, firstNon-empty VerificationBut this is complicated because dWarp2 can accept null in many cases. This non-null judgment is only used to avoid the automatic unpacking exception of the compiler.
2.Avoid using the three-object OperatorHowever, it is estimated that many people will not be able to discard the three targets (I am also one ).
Double dWarp1 = null; boolean flag =true; Double dWarp2 = 0d; if (flag) { dWarp2 = dWarp1; }
3.Types in unified operationsTo avoid mixing the types. (I personally think this is more elegant)
Double dWarp1 = null; boolean flag =true; DoubledWarp2 = (flag) ? dWarp1 : Double.valueOf(0);