Issue an overview

As always, don’t write too much if.. for the sake of code elegance. else… “, so a crackling operation, write a ternary expression to get the value, a run unexpectedly reported an error. The code is as follows:

public static void main(String[] args) {
    Integer a = null;
    boolean flag = false;
    Integer b = flag ? 1 : a;
}
Copy the code

The code is simple, just a normal ternary expression. As expected, when flag is true, autobox 1 assigns a value to B; When flag is flase, null is returned to B. But, after running the code is called the null pointer Exception: the Exception in the thread “is the main” Java. Lang. NullPointerException. I’ve been writing ternary expressions for years. This is the first time I’ve seen this.

Problem analysis

So why is there a null pointer?

If we assign an Integer to a 1 or null, and the same null-pointer exception occurs when we use Object directly to receive a ternary expression, then the problem arises with the data type conversion on the ternary expression.

Ternary expression data type conversions have the following rules:

  1. If the two operands cannot be converted to each other, the type of Object is returned.

    For example, Object b = flag? new String() : new Integer(1024); String and Integer are two different types of objects and cannot be converted to each other.

  2. If there are two operands: S, T, and S contains T, the final value of type S is returned.

    Code examples:

    public static void main(String[] args) {
            Integer a = 1024;
            boolean flag = true;
            Object b1 = flag ? 1 : a;
            Object b2 = flag ? a : 1;
    
            System.out.println("b1: " + b1 + ", b1 instanceof Integer: " + (b1 instanceof Integer));
            System.out.println("b2: " + b2 + ", b2 instanceof Integer: " + (b2 instanceof Integer));
    }
    Copy the code

    Running results:

    b1: 1, b1 instanceof Integer: true
    b2: 1024, b2 instanceof Integer: true
    Copy the code

    As you can see from the above results, the final Object type returned is Integer, because Integer contains both null and int values.

  3. If the two operands can be converted to each other, the transition is upward.

    Code examples:

    public static void main(String[] args) {
            Integer a = 1024;
            boolean flag = true;
            Object b1 = flag ? 1L : a;
            Object b2 = flag ? a : 1L;
    
         System.out.println("b1: " + b1 + ", b1 instanceof Long: " + (b1 instanceof Long));
         System.out.println("b1: " + b1 + ", b1 instanceof Integer: " + (b1 instanceof Integer));
         System.out.println("b2: " + b2 + ", b2 instanceof Long: " + (b2 instanceof Long));
         System.out.println("b2: " + b2 + ", b2 instanceof Integer: " + (b2 instanceof Integer));
        }
    Copy the code

    Running results:

     b1: 1, b1 instanceof Long: true
     b1: 1, b1 instanceof Integer: false
     b2: 1024, b2 instanceof Long: true
     b2: 1024, b2 instanceof Integer: false
    Copy the code

    As can be seen from the above results, the Object returned by the ternary expression is all of type Long. Even if the expression operation results return an Object with value Integer, the expression is eventually unpacked and converted to type Long.

Now that we know the rules for ternary expressions, let’s go back to the code that initially raised the null pointer exception:

public static void main(String[] args) {
    Integer a = null;
    boolean flag = false;
    Integer b = flag ? 1 : a;
}
Copy the code

Based on the operand types in the expression above, the second matching rule should be used, that is, the Integer type with a large range should be returned.

Let’s decompile the bytecode file using javap -c test. class.

Integer.valueof () returns a new Integer object to B. Therefore, when an object of type Integer a is null, the intValue() method on that object cannot be executed, so a null-pointer exception is thrown.

Integer.java

 public final class Integer extends Number implements Comparable<Integer> {
 	private final int value;

    public int intValue(a) {
        return value;
    }

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
        return newInteger(i); }}Copy the code

The type of the operand is the same as that of the operand.

public static void main(String[] args) {
        Integer a = null;
        boolean flag = false;
        Integer b = flag ? new Integer(1) : a;

        System.out.println("b1: " + b + ", b1 instanceof Integer: " + (b instanceof Integer));
    }
Copy the code

Running results:

b1: null, b1 instanceof Integer: false
Copy the code

conclusion

This time in the ternary expression on the pit, should be a lot of people have encountered. This is not a syntax error, so the compiler cannot catch it, and it cannot be exposed if the runtime is not null, so we need to be careful when writing ternary expressions.

To avoid this situation, lay your foundation and be familiar with the data conversion rules of ternary expressions. In the implementation process, try to use operands of the same type, and at the same time do a good job of nullvalue testing of various data types.