美文网首页
一个三目运算符引发的“血案”

一个三目运算符引发的“血案”

作者: 董家二少 | 来源:发表于2019-07-05 18:30 被阅读0次

    问题描述

    今天有个同事发过来一个消息,说他的系统今天老是报错误,代码看了好几次都没发现问题,百思不得其解,让我帮忙看眼。我拿到代码后,乍一看也没看出个所以然,但是在执行单元测试之后,问题很快就暴露出来了,坑啊~~~~ 大致代码如下:

            class Operator{
                Long cityIdCharge;
                .....
            }
    
            ......
            
            Operator operator = new Operator();
            Long currentCityId = operator == null ? 0L : operator.getCityInCharge();
            System.out.println(currentCityId);
    

    在执行如上代码时,将会抛出NPE异常,而且错误位于代码第二行。那为什么执行第二行代码会出现NPE错误呢,下面我们来分析一下。

    原因分析

    根据异常堆栈,可以看到,异常定位于第二行代码。第二行代码是一个三元表达式,我们知道三目运算符只要后面两个是不同的类型,涉及到类型转换,那么编译器就会向下(基本类型)进行转型,再进行计算。换言之,如果在计算表达式中,有Integer和int,那么Integer类型会先转化为int再进行计算。
    在开始分析代码之前,需要回顾一下java的基本数据类型。long是8种基本数据类型之一,Long是long的包装类,继承于Number类。
    在示例代码中,operator.getCityInCharge()返回的是Long类型,值为null。三元表达式的第二个值是0L,我们知道,Long的默认值是null.long的默认值是0L,因此,对于上述示例代码中的第二行代码,Long和long不是同一个类型,编译器会默认向下转型,Long转为long,但是此时Long类型对应的值为null,因此抛出NPE。

    Long与long之间的相互转化

    1. 拆箱操作
      可以使用longValue()方法将Long变为long
     Long a = 1L;
     long b = a.longValue();
    

    也可以直接进行拆箱操作

    long c = a;

    1. 装箱操作
      可以使用new方法将long变成Long

    long a = 1L;
    Long b = new Long(a);
    也可以直接进行装箱操作
    Long c = a;

    因此上述的示例代码可以修改为

    Long currentCityId = operator == null ? new Long(0) : operator.getCityInCharge();
    

    或者使用Optional结构对operator.getCityInCharge()进行封装后再进行计算

    Optional.ofNullable(operator.getCityInCharge()).orElse(0L)
    

    写在最后

    1. 在使用对象时,需要先验空,再进行操作处理,也可以使用guava中的Optional对对象进行封装
    2. 使用三元运算符时最后使用相同类型的对象
    3. 一般来说,属性的值建议使用封装类型,临时变量建议使用基本类型

    相关文章

      网友评论

          本文标题:一个三目运算符引发的“血案”

          本文链接:https://www.haomeiwen.com/subject/klzehctx.html