有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

nullpointerexception Java:如何避免三元运算符中的NPE,通常如何优雅地执行nullchecks?

昨天我不得不编写一段难看的代码,以便对对象的字段执行许多空检查,以避免三元运算符构造的NPE

有问题的代码:

ResourceThresholds rt = getThresholdsFromConfig();
Thresholds defaultPerContainer = getDefaultThresholds();
    
return new Thresholds((!rt.getCpu().equals("")) ? Long.parseLong(rt.getCpu()) : defaultPerContainer.getCpu(),
           (!rt.getMemory().equals("")) ? Long.parseLong(rt.getMemory())  : defaultPerContainer.getMemory(),/*omitted for brevity*/);

我在defaultPerContainer.getCpu()上得到一个NPE,因为字段cpu = null。 这很好,Java就是这样工作的。 为什么我没有默认设置字段Long cpu = 0L;?因为我需要null值作为我们不设置任何值的指示符

这段特定代码的最终功能变体是:

        Long cpuVal;
        if (!rt.getCpu().equals("")) {
            cpuVal = Long.parseLong(rt.getCpu());
        } else {
            cpuVal = defaultPerContainer.getCpu();
        }
        Long memory;
        if (!rt.getMemory().equals("")) {
            memory = Long.parseLong(rt.getMemory());
        } else {
           memory = defaultPerContainer.getMemory();
        }
        //... many similar if-elses that give me the desired value;
        //which is really ugly, and I believe I am not the only one hitting this.
        return new Thresholds(cpuVal, memory..);

这段代码按照我需要的方式工作,但它很难看

问题1:有人能告诉我,我是否能找到一种方法,使用Optional<T>用三元运算符解析第一个变量中的NPE?因为这个代码段是有效的:!rt.getCpu().equals("")) ? Long.parseLong(rt.getCpu()) : null也就是说,如果我显式地把null作为一个值,当条件满足时,我会得到null

一般来说,有什么优雅的Java8+方法可以解决这个问题吗

问题2:如何优化用于空检查的if-else构造


共 (2) 个答案

  1. # 1 楼答案

    问题是,在三元表达式A ? B : C中,如果BC都是兼容的数字类型,但一个是装箱对象,另一个是原语,大多数人会认为通过自动装箱原语,结果是装箱的

    事实并非如此。三元运算符会取消对象的绑定,因此它们都是基元,结果就是基元

    这意味着以下内容是相同的:

    long B = ...;
    Long C = ...;
    
    Long R = ... ? B : C;
    
    Long R = (Long) (... ? B : (long) C);
    

    结果是,如果C为空,则得到NPE

    解决这个问题的一种方法是强制B自动装箱:

    Long R = ... ? (Long) B : C;
    

    通过这种更改,空的C值只需设置R = null

    在问题中的例子中,BLong.parseLong(rt.getCpu()),因此与其添加强制自动装箱的强制转换,不如使用^{}

    另外,不相关的,用isEmpty()代替equals(""),并且A周围不需要括号

    将代码更改为:

    return new Thresholds(!rt.getCpu().isEmpty() ? Long.valueOf(rt.getCpu()) : defaultPerContainer.getCpu(),
                          !rt.getMemory().isEmpty() ? Long.valueOf(rt.getMemory())  : defaultPerContainer.getMemory(),
                          /*omitted for brevity*/);
    
  2. # 2 楼答案

    1. 代码片段中没有null检查
    2. 最好实现一个简单的实用方法,并在设置cpumemory时使用它,如果valnull,则使用Joda条件"".equals(val)来防止NPE
    3. 要避免取消装箱,请使用Long.valueOf而不是返回原语longLong.parseLong
    public static Long getValue(String val, Long defaultValue) {
        return "".equals(val) ? defaultValue : Long.valueOf(val);
    }
    
    Long cpuVal = getValue(rt.getCpu(), defaultPerContainer.getCpu());
    Long memory = getValue(rt.getMemory(), defaultPerContainer.getMemory());
    

    还可以使用参数提供者提供重载实用程序方法,然后将方法引用传递给它:

    public static Long getValue(Supplier<String> str, Supplier<Long> defVal) {
        return getValue(str.get(), defVal.get()); // calling implementation above 
    }
    
    Long cpuVal = getValue(rt::getCpu, defaultPerContainer::getCpu);
    Long memory = getValue(rt::getMemory, defaultPerContainer::getMemory);