在一行代码中,通过一个方法调用检查返回值是否不为空,如果是,则将其赋值。

108

Java中到处都充斥着像这样的语句:

if(cage.getChicken() != null) {
    dinner = cage.getChicken();
} else {
    dinner = getFreeRangeChicken();
}

需要两次调用 getChicken() 才能将返回的对象分配给 dinner

也可以写成一行,如下所示:

dinner = cage.getChicken() != null? cage.getChicken() : getFreeRangeChicken();

可惜的是,还有两个对getChicken()的调用。

当然,我们可以先分配一个本地变量,然后再次使用三元运算符进行赋值(如果不为空),但这需要两行代码,看起来不太美观:

FutureMeal chicken = cage.getChicken();
dinner = chicken != null? chicken : getFreeRangeChicken();

那么有没有一种方法可以这样写:

如果某个值不为空,则变量var = 某个值,否则变量var = 另一个值;

我猜我只是在谈论语法,在编译代码之后,它可能在性能方面并没有太大区别。

由于这是如此常见的代码,有一个一行式的代码会很棒。

其他语言是否具备此功能?


4
这并不是一个答案,但如果你的代码充满了这样的代码块来替换丢失的值为默认值,那么你的API设计可能存在问题。 - Denys Séguret
9个回答

97

和Loki的回答原理相同,只是更简短。但要记住,简短并不意味着更好。

dinner = Optional.ofNullable(cage.getChicken())
  .orElse(getFreerangeChicken());
注意:JDK的架构师和 Optional 功能的设计者明确反对使用 Optional 的这种用法。每次都会分配一个新对象并立即放弃它。但另一方面,这样做也可能更易读。
注意:JDK的架构师和Optional功能的设计者明确反对使用Optional的这种用法。每次都会分配一个新对象并立即放弃它。但另一方面,这样做也可能更易读。

17
如果 getFreerangeChicken() 很昂贵,请确保使用 dinner = Optional.ofNullable(cage.getChicken()).orElseGet(()->getFreerangeChicken()); - Matheus208
1
在 WebSocket 客户端中,javax.websocket.CloseReason.getReasonPhrase() 对于成功的消息返回 null 是有用的。 - Stuart Cardall
1
太遗憾了,它被不鼓励使用。我有点喜欢它。 - Pieter De Bie
@PieterDeBie 为什么不鼓励这样做? - Heather Sawatsky
3
@HeatherSawatsky,我认为只是因为,正如这个答案的发布者所指出的那样,每次使用它时都会分配一个全新的Optional对象,因此比自己检查它是否为空更昂贵。 - Alex Pritchard

92

Java缺乏合并运算符,因此您的代码需要使用显式的临时变量,这是在单个调用中进行赋值的最佳选择。

您可以将结果变量用作临时变量,就像这样:

dinner = ((dinner = cage.getChicken()) != null) ? dinner : getFreeRangeChicken();

然而,这很难阅读。


1
工作效果符合我的预期,但我的IDE(Android Studio)要求我在“重复初始化”的标题下将其删除... - MoxGeek

36
自Java 9以来,你可以使用Objects#requireNonNullElse方法,其功能为:
public static <T> T requireNonNullElse(T obj, T defaultObj) {
    return (obj != null) ? obj : requireNonNull(defaultObj, "defaultObj");
}

你的代码将会是:

dinner = Objects.requireNonNullElse(cage.getChicken(), getFreeRangeChicken());

这一行只调用了getChicken()一次,因此两个要求都得到满足。

请注意,第二个参数也不能为null;这个方法强制返回值的非空性。

还考虑另一种方式:Objects#requireNonNullElseGet

public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)

如果第一个参数不是null,则不会评估第二个参数,但是会产生创建Supplier的开销。


23

为什么不使用Java 8? - phil294
Java 8有Optional,不需要外部依赖。当然你仍然可以使用它。 - Pieter De Bie
一个好的例子就是在为 Android API 24 之前版本开发时,Optional 调用不可用。 - Levite
2
ObjectUtils 还提供了 firstNonNull(cage.getChicken(),getFreeRangeChicken()),在我看来更易读,并允许更长的回退链。 - Joachim Lous
1
不行,因为这样会计算getChicken()和getFreeRangeChicken()两个函数,而根据问题的要求只需要计算一个(if-else)。 - Flyout91
原问题在于他必须执行两次cage.getChicken()。并不是他同时执行了cage.getChicken()和getFreeRangeChicken()。 - Pieter De Bie

12

使用Java 1.8,您可以使用Optional

public class Main  {

    public static void main(String[] args) {

        //example call, the methods are just dumb templates, note they are static
        FutureMeal meal = getChicken().orElse(getFreeRangeChicken());

        //another possible way to call this having static methods is
        FutureMeal meal = getChicken().orElseGet(Main::getFreeRangeChicken); //method reference

        //or if you would use a Instance of Main and call getChicken and getFreeRangeChicken
        // as nonstatic methods (assume static would be replaced with public for this)
        Main m = new Main();
        FutureMeal meal = m.getChicken().orElseGet(m::getFreeRangeChicken); //method reference

        //or
        FutureMeal meal = m.getChicken().orElse(m.getFreeRangeChicken()); //method call


    }

    static Optional<FutureMeal> getChicken(){

        //instead of returning null, you would return Optional.empty() 
        //here I just return it to demonstrate
        return Optional.empty();

        //if you would return a valid object the following comment would be the code
        //FutureMeal ret = new FutureMeal(); //your return object
        //return Optional.of(ret);            

    }

    static FutureMeal getFreeRangeChicken(){
        return new FutureMeal();
    }
}

你需要为getChicken方法编写逻辑,使其返回Optional.empty()而不是null,或者返回Optional.of(myReturnObject),其中myReturnObject是你的chicken对象。

然后,当你调用getChicken()方法时,如果它返回Optional.empty()orElse(fallback)方法会给你一个回退值,而在你的情况下就是第二个方法。


9

使用自己的

public static <T> T defaultWhenNull(@Nullable T object, @NonNull T def) {
    return (object == null) ? def : object;
}

例子:

defaultWhenNull(getNullableString(), "");

优点

  • 适用于不使用Java8的开发
  • 支持API 24之前的Android开发设备
  • 不需要外部库的支持

缺点

  • 总是评估默认值(而不是cond?nonNull():notEvaluated()

    可以通过传递Callable而不是默认值来规避此问题,但这会使其变得更加复杂和不太动态(例如,如果性能成为问题)。

    顺便说一下,当使用Optional.orElse()时也会遇到同样的缺点 ;-)


0
dinner = cage.getChicken();
if(dinner == null) dinner = getFreeRangeChicken();

或者

if( (dinner = cage.getChicken() ) == null) dinner = getFreeRangeChicken();

除了缺少花括号之外,在评估中使用赋值是不合法的,我认为。这段代码太难读懂了,而且很容易出错。 - Jan Groth
虽然这可能回答了问题,但最好在您的答案中加入一些文本来解释您正在做什么。请阅读如何撰写良好的答案 - Jørgen R
1
我认为答案的第一部分是最好的解决方案,它清晰易懂,并且只调用了一次getChicken函数。 - NikkyD

0

你可以使用

Objects.requireNonNullElseGet(cage.getChicken(), () -> getFreerangeChicken())

使用静态导入会更加方便:

import static java.util.Objects.requireNonNullElseGet;

requireNonNullElseGet(cage.getChicken(), () -> getFreerangeChicken())

(*) 不适用于Java 1.8。 - andreszs
3
这将在每次调用时评估两个函数,因为传递的是结果而不是方法引用。最好使用 Objects.requireNonNullElseGet(cage.getChicken(), () -> getFreeRangeChicken());,它只会在第一个对象为空时调用供应商。 - Michael
这只是 https://dev59.com/xl0b5IYBdhLWcg3wJ-bb#52063580 中答案的更差版本。 - user1803551
修正了答案。之前打这个的时候我不知道自己在想什么... - Datz

-1

在Java8中,您可以根据需要使用NullableNotNull注释。

 public class TestingNullable {
        @Nullable
        public Color nullableMethod(){
            //some code here
            return color;
        }

        public void usingNullableMethod(){
            // some code
            Color color = nullableMethod();
            // Introducing assurance of not-null resolves the problem
            if (color != null) {
                color.toString();
            }
        }
    }

 public class TestingNullable {
        public void foo(@NotNull Object param){
            //some code here
        }

        ...

        public void callingNotNullMethod() {
            //some code here
            // the parameter value according to the explicit contract
            // cannot be null
            foo(null);
        }
    }

http://mindprod.com/jgloss/atnullable.html


2
如果楼主的目标是为了防止混乱,那么您只是让问题变得更糟。 - andreszs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接