三目运算符结合包装类使用时候注意类型对齐

三目运算符的类型对齐:

看阿里巴巴开发手册中有如下描述:

三目运算符 condition? 表达式 1 : 表达式 2 中,高度注意表达式 1 和 2 在类型对齐 时,可能抛出因自动拆箱导致的 NPE 异常。

还附有例子:

说明:以下两种场景会触发类型对齐的拆箱操作:

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

Integer a = 1;
Integer b = 2;
Integer c = null;
Boolean flag = false;
// a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
// 如果因为flag的原因, 结果没有选择c, 那么也不会触发NPE
Integer result=(flag? a*b : c);

果断IDE中运行一下,确实抛出抛出NPE;但如果修改一下代码

Integer result=(flag? a*b : null);

则不会抛出NPE;这里猜测是对null有特殊处理; 另外这里将flag改为true, 即让result = a * b, 则不会触发NPE;

再来理解一下类型对齐,看描述的意思,表达式1和表达式2需要是类型一致的,那么:

  1. 如果表达式1和2都是非包装类的引用对象,那么类型一致只可能是子类转为父类,如果其没有父子关系,那编译都不会通过;这里就没有太多问题,即便引用是null的子类引用,转换成父类引用也不会有问题;
  1. 这种场景需要注意的就是包装类型了,表达式 1 或表达式 2 的值只要有一个是原始类型的我们已经体验到了;

     // 会抛出NPE异常
     Integer s = null;
     Integer i = false ?  1 : s;
    
     // 改成如下代码则不会抛出异常;
     Integer s = null;
     Integer i = false ?  Integer.valueOf(1) : s;
    
  2. 表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型,见如下代码:

     // 会抛出NPE异常
     Integer s = null;
     Long i = false ?  Long.valueOf(1) : s;
    

直接将Integer赋值给Long是不能通过编译的,而通过三目运算符,因为其类型对齐的原因,其会将s转换成Long,所以上面的表达式可以正常执行并抛出NPE;

但三目运算符中s改为null,则不会抛出NPE;

// 不会抛出NPE异常
Long i = false ?  Long.valueOf(1) : null;

综上,三目运算符其实要注意的场景就是阿里巴巴开发手册中描述到的两种场景,且只发生在包装类上:

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

注意:

当我写如下语句时:

Integer i = true ?  Long.valueOf(1) : s;

编译器提示的内容如下:

Incompatible types.
Required:
java.lang.Integer
Found:
long

这意味着后面即便是包装类,但返回的还是基本类型;这意味着 就算我将i改为Long,也意味着,这里会出现拆箱、装箱操作:

Integer s = null;
Long number = 1L;
Long i = true ?   number: s;

通过IDE调试发现,这段代码出现了多次装、拆箱:

  1. 首先number = 1L时候装箱,这个是在意料之中的;
  2. Long i = true ? number: s; 这一句出现了将number 拆箱为long,然后再给i赋值的时候装箱为Long;

如此看来,三目运算符对于包装类的操作,都会先转换成基本类型,然后根据返回值的接收对象是否是包装类再决定是否装箱,值得注意的是,这里的s因为不满足条件(直接将number赋值给i,没管s),并没有对其进行装拆箱操作;

你可能感兴趣的:(三目运算符结合包装类使用时候注意类型对齐)