Java“?”检查null的运算符 - 它是什么? (不是三元!)

时间:2010-12-08 17:01:55

标签: java syntax null

我正在阅读一篇与斜线故事相关联的文章,并发现了这个小故事:

  

使用最新版本的Java   尝试进行空指针检查   通过提供简写语法更容易   对于无尽的指针测试。只是   为每个方法添加问号   调用自动包含一个   测试空指针,替换a   大鼠的if-then语句的嵌套,如此   如:   

    public String getPostcode(Person person) {
      String ans= null;
      if (person != null) {
        Name nm= person.getName();
        if (nm!= null) {
          ans= nm.getPostcode();
        }
      }
      return ans
    } 

     

有了这个:   

public String getFirstName(Person person) {
      return person?.getName()?.getGivenName();
    } 

我在互联网上搜索过(好吧,我花了至少15分钟在谷歌问号上搜索变种)并没有得到任何结果。所以,我的问题是:有关于此的官方文件吗?我发现C#有一个类似的运算符(“??”运算符),但是我想得到我正在使用的语言的文档。或者,这只是我使用的三元运算符从未见过。

谢谢!

编辑:链接到文章:http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292

15 个答案:

答案 0 :(得分:66)

最初的想法来自groovy。作为Project Coin的一部分,Java 7被提议:https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC(Elvis和其他Null-Safe Operators),但尚未被接受。

相关的Elvis运算符?:被提议为x ?: y提供x != null ? x : y简写,当x是复数表达式时尤其有用。

答案 1 :(得分:52)

这种语法在Java中不存在,也不会被包含在我所知道的任何即将发布的版本中。

答案 2 :(得分:16)

在Java 7中有一个提议,但它被拒绝了:

http://tech.puredanger.com/java7/#null

答案 3 :(得分:15)

解决缺乏"?"的一种方法。使用Java 8的运算符没有try-catch的开销(它也可以隐藏在其他地方发起的NullPointerException,如上所述)是创建一个类来管道" Java-8-Stream风格的方法。

public class Pipe<T> {
    private T object;

    private Pipe(T t) {
        object = t;
    }

    public static<T> Pipe<T> of(T t) {
        return new Pipe<>(t);
    }

    public <S> Pipe<S> after(Function<? super T, ? extends S> plumber) {
        return new Pipe<>(object == null ? null : plumber.apply(object));
    }

    public T get() {
        return object;
    }

    public T orElse(T other) {
        return object == null ? other : object;
    }
}

然后,给定的例子将成为:

public String getFirstName(Person person) {
    return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get();
}

<强> [编辑]

经过进一步思考,我发现只有使用标准的Java 8类才能实现相同的目标:

public String getFirstName(Person person) {
    return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null);
}

在这种情况下,通过将其作为"<no first name>"的参数传递,甚至可以选择默认值(例如null)而不是orElse

答案 4 :(得分:7)

请参阅:https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so(特别是“Elvis和其他null安全操作符”)。

结果是Java 7考虑了此功能,但未包括在内。

答案 5 :(得分:6)

实际上是Groovy's safe-dereference operator。你不能在纯Java中使用它(遗憾的是),因此帖子完全错误(或者更有可能是误导,如果它声称Groovy是“最新版本的Java”)。

答案 6 :(得分:2)

可以使用Java 8 lambda来定义以几乎漂亮的方式解决这个问题的util方法。

这是H-MANs solution的变体,但它使用带有多个参数的重载方法来处理多个步骤,而不是捕获NullPointerException

即使我认为这个解决方案很酷,但我认为我更喜欢Helder Pereira's秒,因为它不需要任何util方法。

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

答案 7 :(得分:1)

我不确定这甚至会起作用;如果,例如,person引用为null,运行时将用什么替换它?一个新人?这将要求Person有一些你在这种情况下期望的默认初始化。您可以避免空引用异常,但如果您没有计划这些类型的设置,则仍会出现不可预测的行为。

?? C#中的运算符可能最好称为“coalesce”运算符;你可以链接几个表达式,它将返回第一个非null。不幸的是,Java没有它。我认为你能做的最好的事情就是使用三元运算符来执行空检查,如果链中的任何成员为null,则评估整个表达式的替代方法:

return person == null ? "" 
    : person.getName() == null ? "" 
        : person.getName().getGivenName();

你也可以使用try-catch:

try
{
   return person.getName().getGivenName();
}
catch(NullReferenceException)
{
   return "";
}

答案 8 :(得分:1)

Java没有完全语法,但是从JDK-8开始,我们可以使用各种方法Optional API。所以,使用null conditional operator的C#版本:

return person?.getName()?.getGivenName(); 

可以用Java编写如下Optional API

 return Optional.ofNullable(person)
                .map(e -> e.getName())
                .map(e -> e.getGivenName())
                .orElse(null);

如果persongetNamegetGivenName中的任何一个为null,则返回null。

答案 9 :(得分:0)

你有它,Java 8中的空安全调用:

public void someMethod() {
    String userName = nullIfAbsent(new Order(), t -> t.getAccount().getUser()
        .getName());
}

static <T, R> R nullIfAbsent(T t, Function<T, R> funct) {
    try {
        return funct.apply(t);
    } catch (NullPointerException e) {
        return null;
    }
}

答案 10 :(得分:0)

如果有人正在寻找旧版Java的替代品,你可以尝试我写的那个:

/**
 * Strong typed Lambda to return NULL or DEFAULT VALUES instead of runtime errors. 
 * if you override the defaultValue method, if the execution result was null it will be used in place
 * 
 * 
 * Sample:
 * 
 * It won't throw a NullPointerException but null.
 * <pre>
 * {@code
 *  new RuntimeExceptionHandlerLambda<String> () {
 *      @Override
 *      public String evaluate() {
 *          String x = null;
 *          return x.trim();
 *      }  
 *  }.get();
 * }
 * <pre>
 * 
 * 
 * @author Robson_Farias
 *
 */

public abstract class RuntimeExceptionHandlerLambda<T> {

    private T result;

    private RuntimeException exception;

    public abstract T evaluate();

    public RuntimeException getException() {
        return exception;
    }

    public boolean hasException() {
        return exception != null;
    }

    public T defaultValue() {
        return result;
    }

    public T get() {
        try {
            result = evaluate();
        } catch (RuntimeException runtimeException) {
            exception = runtimeException;
        }
        return result == null ? defaultValue() : result;
    }

}

答案 11 :(得分:0)

您可以测试您提供的代码,它会产生语法错误。因此,Java不支持它。 Groovy确实支持它,它被提议用于Java 7(但从未被包含在内)。

但是,您可以使用Java 8中提供的Optional。这可以帮助您在类似的行上实现某些功能。 https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

Example Code for Optional

答案 12 :(得分:0)

由于除非安装的OS> = 24,否则Android不支持Lambda函数,因此我们需要使用反射。

// Example using doIt function with sample classes
public void Test() {
    testEntry(new Entry(null));
    testEntry(new Entry(new Person(new Name("Bob"))));
}

static void testEntry(Entry entry) {
    doIt(doIt(doIt(entry,  "getPerson"), "getName"), "getName");
}

// Helper to safely execute function 
public static <T,R> R doIt(T obj, String methodName) {
    try {
       if (obj != null) 
           return (R)obj.getClass().getDeclaredMethod(methodName).invoke(obj);
    } catch (Exception ignore) {
    }
    return null;
}
// Sample test classes
    static class Entry {
        Person person;
        Entry(Person person) { this.person = person; }
        Person getPerson() { return person; }
    }

    static class Person {
        Name name;
        Person(Name name) { this.name = name; }
        Name getName() { return name; }
    }

    static class Name {
        String name;
        Name(String name) { this.name = name; }
        String getName() {
            System.out.print(" Name:" + name + " ");
            return name;
        }
    }
}

答案 13 :(得分:0)

很多答案都提到Java语言没有这个功能。

使用此 compiler plugin 在少数情况下是可能的,几乎没有限制

在你提到的示例代码中可以写成

public String getFirstName(Person person) {
  @NullSafe
  String retVal = person.getName().getGivenName();
  return retVal;
} 

PS:我是插件的作者

答案 14 :(得分:-3)

如果这不是您的性能问题,您可以写

public String getFirstName(Person person) {
  try {
     return person.getName().getGivenName();
  } catch (NullPointerException ignored) {
     return null;
  }
}