为什么阿里巴巴要开发者高度注意三目运算符?

636 阅读5分钟

引言

三目运算符大家都应该了解吧。是一种简化代码的操作。是很多编程语言都支持的一个写法。用法也很简单。

condition ? 表达式1 : 表达式2

  1. 当condition为true的时候,执行表达式1。
  2. 当condition为false的时候,执行表达式2。

自动拆装箱大家应该也都熟悉。Java中推出了基本类型相对应的包装类。

  • byte <--> Byte
  • short <--> Short
  • char <--> Character
  • int <--> Integer
  • long <--> Long
  • float <--> Float
  • double <--> Double
  • boolean <--> Boolean

自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。

倘若我拿出三目运算符+自动拆装箱,阁下又该如何应对呢?

image.png

这就是阿里巴巴特别强调的一个地方。

image.png

条件触发

这里描述的是因为表达式1和2在类型对齐时,可能抛出因自动拆箱导致的NPE异常。并且是有两种场景会触发类型对齐的拆箱操作:

  1. 表达式1或表达式2的值只要有一个是原始类型。
  2. 表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。

不过这里的代码大家可能没看懂。为啥a*b的结果是int类型?而C为啥又会被强制拆箱成int类型?

NPE异常

这是因为在Java中,当进行两个整数(Integer)的数学运算时,结果通常会是int类型,而不是Integer类型。这是因为自动拆箱和装箱操作会导致Integer被拆箱为int进行运算,然后再装箱为Integer。这种自动拆箱和装箱的操作在Java中被广泛应用,以便将基本数据类型和包装类之间进行转换。

public class ThreeOperate {

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = null;
        Boolean flag = false;
        // a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
        Integer result = flag ? a * b : c;
    }
}

点击运行。出现NPE。

Exception in thread "main" java.lang.NullPointerException
	at ThreeOperate.main(ThreeOperate.java:9)

结合我们前面所说的,我们将这段代码进行反编译。

public class ThreeOperate {

   public static void main(String[] args) {
      // 自动装箱
      Integer a = Integer.valueOf(1);
      Integer b = Integer.valueOf(2);
      Object c = null;
      Boolean flag = Boolean.valueOf(false);
      //a*b拆箱计算
      //c类型对齐,进行拆箱
      //对结果进行自动装箱
      Integer result = Integer.valueOf(flag.booleanValue()?a.intValue() * b.intValue():((Integer)c).intValue());
   }
}

我们可以发现的确是两个Integer进行数学运算时,是首先会调用intValue()拆箱成int类型进行计算。然后将结果通过Integer.valueOf()包装成包装类。

同时我们也可以清楚的知道了为啥会出现NPE。原因是获取Integer类型C对象的intValue()时,C为null。所以就NPE了。

场景验证

前面场景1:表达式1或表达式2的值只要有一个是原始类型。就可能出现NPE。而给出的例子也是属于场景1的。不过是更加隐晦了。两个Integer对象进行了数据运算是通过int来计算的。

场景1

我们来看看比较直白的例子:

public class ThreeOperate {

        public static void main(String[] args) {
        int a = 1;
        Integer c = null;
        Boolean flag = false;
        // a 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
        Integer result = flag ? a  : c;
    }
}

反编译

public class ThreeOperate {

   public static void main(String[] args) {
      byte a = 1;
      Object c = null;
      Boolean flag = Boolean.valueOf(false);
      Integer result = Integer.valueOf(flag.booleanValue()?a:((Integer)c).intValue());
   }
}

同样是c自动拆箱导致NPE异常。

接着我们看这个场景的反例:两个条件都没有基本数据类型

public class ThreeOperate {

    public static void main(String[] args) {
        Integer a = 1;
        Integer c = null;
        Boolean flag = false;
        Integer result = flag ? a : c;
    }
}

运行正常。反编译之后。

public class ThreeOperate {

   public static void main(String[] args) {
      // 自动装箱
      Integer a = Integer.valueOf(1);
      Object c = null;
      Boolean flag = Boolean.valueOf(false);
      Integer var10000 = (Integer)(flag.booleanValue()?a:c);
   }
}

没有进行自动拆箱操作。

场景2

表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。

  • byte -> short -> int -> long -> float -> double
  • char -> int
public class ThreeOperate {
    
    public static void main(String[] args) {
        Double a = 2.0;
        Integer c = null;
        Boolean flag = false;
        Double result = flag ? a: c;
    }
}

运行。又是熟悉的NPE。

Exception in thread "main" java.lang.NullPointerException
	at ThreeOperate.main(ThreeOperate.java:9)

反编译

public class ThreeOperate {

   public static void main(String[] args) {
      Double a = Double.valueOf(2.0D);
      Object c = null;
      Boolean flag = Boolean.valueOf(false);
      Double result = Double.valueOf(flag.booleanValue()?a.doubleValue():(double)((Integer)c).intValue());
   }
}

c首先会进行拆箱操作,然后强转为Double对象。由于c为null,所以出现空指针。

总结

  1. 装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。
  2. 当包装类进行数学运算时会自动拆箱使用基本数据类型进行计算。

有两种场景会触发类型对齐的拆箱操作:

  1. 表达式1或表达式2的值只要有一个是原始类型。
  2. 表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。