检查 null 的操作符-它是什么? (不是三进制!)

我在读一篇关于 slashdot 的文章,发现了一个小花絮:

以 Java 的最新版本为例 尝试进行空指针检查 通过提供速记语法更容易 没完没了的指针测试 给每个方法加一个问号 调用自动包括一个 测试空指针,替换 老鼠窝里的 if-then 语句,例如 作为:

Public String getPostcode (Person Person){
字符串 ans = null;
如果(人! = 空){
Name nm = person. getName () ;
如果(nm! = null){
Ans = nm.getPostcode () ;
}
}
返回答案
}

用这个:

Public String getFirstName (Person Person){
返回人? . getName () ? . getGivenName () ;
}

我在网上搜索了一番(好吧,我至少花了15分钟在谷歌上搜索“ java 问号”的变体) ,但一无所获。所以,我的问题是,有关于这个的官方文件吗?我发现 C # 有一个类似的运算符(“ ? ?”)运算符) ,但是我想获得我正在使用的语言的文档。或者,这仅仅是我从未见过的三元运算符的一种用法。

谢谢!

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

168696 次浏览

This syntax does not exist in Java, nor is it slated to be included in any of the upcoming versions that I know of.

That's actually Groovy's safe-dereference operator. You can't use it in pure Java (sadly), so that post is simply wrong (or more likely slightly misleading, if it's claiming Groovy to be the "latest version of Java").

There was a proposal for it in Java 7, but it was rejected:

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

See: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (specifically "Elvis and other null safe operators").

The result is that this feature was considered for Java 7, but was not included.

The original idea comes from groovy. It was proposed for Java 7 as part of Project Coin: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Elvis and Other Null-Safe Operators), but hasn't been accepted yet.

The related Elvis operator ?: was proposed to make x ?: y shorthand for x != null ? x : y, especially useful when x is a complex expression.

I'm not sure this would even work; if, say, the person reference was null, what would the runtime replace it with? A new Person? That would require the Person to have some default initialization that you'd expect in this case. You may avoid null reference exceptions but you'd still get unpredictable behavior if you didn't plan for these types of setups.

The ?? operator in C# might be best termed the "coalesce" operator; you can chain several expressions and it will return the first that isn't null. Unfortunately, Java doesn't have it. I think the best you could do is use the ternary operator to perform null checks and evaluate an alternative to the entire expression if any member in the chain is null:

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

You could also use try-catch:

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

If this is not a performance issue for you, you can write

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

There you have it, null-safe invocation in 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;
}
}

[EDIT]

Upon further thought, I figured out that it is actually possible to achieve the same only using standard Java 8 classes:

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

In this case, it is even possible to choose a default value (like "<no first name>") instead of null by passing it as parameter of orElse.


[Original Answer]

One way to workaround the lack of "?" operator using Java 8 without the overhead of try-catch (which could also hide a NullPointerException originated elsewhere, as mentioned) is to create a class to "pipe" methods in a Java-8-Stream style.

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;
}
}

Then, the given example would become:

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

It is possible to define util methods which solves this in an almost pretty way with Java 8 lambda.

This is a variation of H-MANs solution but it uses overloaded methods with multiple arguments to handle multiple steps instead of catching NullPointerException.

Even if I think this solution is kind of cool I think I prefer Helder Pereira's seconds one since that doesn't require any util methods.

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; }
}

If someone is looking for an alternative for old java versions, you can try this one I wrote:

/**
* 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;
}


}

You can test the code which you have provided and it will give syntax error.So, it is not supported in Java. Groovy does support it and it was proposed for Java 7 (but never got included).

However, you can use the Optional provided in Java 8. This might help you in achieving something on similar line. 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

Java does not have the exact syntax but as of JDK-8, we have the Optional API with various methods at our disposal. So, the C# version with the use of null conditional operator:

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

can be written as follows in Java with the Optional API:

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

if any of person, getName or getGivenName is null then null is returned.

Since Android does not support Lambda Functions unless your installed OS is >= 24, we need to use reflection.

// 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;
}
}
}

As lot of answers mentioned Java language don't have this feature.

It is possible in few scenarios with this compiler plugin, with few limitations

In the example code you mentioned can be written as

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

PS: I am author of plugin

STEP-1 : Use this generic method.

public static <T> Optional<T> optionalChaining(Supplier<T> resolver) {
try {
T result = resolver.get();
return Optional.ofNullable(result);
} catch (NullPointerException e) {
return Optional.empty();
}
}

STEP-2 one example of usage of this method is below

Optional<String> rs = optionalChaining(()-> user.getName("Talha").getLastName());


if(rs.isPresent()) {
                    

}

Done !!