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

发布于 2024-10-06 20:11:54 字数 842 浏览 4 评论 0原文

我正在阅读 一篇 InfoWorld 文章(链接到 Wayback machine,因为摘录已被删除),并发现了这个小花絮:

采用最新版本的 Java, 尝试进行空指针检查 通过提供简写语法更容易 用于无休止的指针测试。只是 给每个方法加一个问号 调用自动包含一个 测试空指针,替换 if-then 语句的老鼠巢,例如 作为:

public String getPostcode(Person person) {
字符串 ans= null;
如果(人!=空){
姓名 nm= person.getName();
if (nm!= null) {
ans= nm.getPostcode();
}
}
返回答案
} 

这样:

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

我在互联网上进行了搜索(好吧,我花了至少 15 分钟在谷歌上搜索“java 问号”的变体),但一无所获。所以,我的问题是:有这方面的官方文档吗?我发现 C# 有一个类似的运算符(“??”运算符),但我想获取我正在使用的语言的文档。或者,这只是我已经使用的三元运算符的使用以前从未见过。

谢谢!

I was reading an InfoWorld article (link to Wayback machine since the excerpt was since removed), and came across this little tidbit:

Take the latest version of Java, which
tries to make null-pointer checking
easier by offering shorthand syntax
for the endless pointer testing. Just
adding a question mark to each method
invocation automatically includes a
test for null pointers, replacing a
rat's nest of if-then statements, such
as:

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

With this:

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

I've scoured the internet (okay, I spent at least 15 minutes googling variations on "java question mark") and got nothing. So, my question: is there any official documentation on this? I found that C# has a similar operator (the "??" operator), but I'd like to get the documentation for the language I'm working in. Or, is this just a use of the ternary operator that I've never seen before.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

像你 2024-10-13 20:11:55

Java 没有精确语法,但从 JDK-8 开始,我们有可选 API 以及我们可以使用的各种方法。因此,C# 版本使用 null 条件运算符

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

在 Java 中可以使用 可选 API

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

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

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.

恍梦境° 2024-10-13 20:11:55

Java 7 中有一个提案,但被拒绝了:

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

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

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

影子是时光的心 2024-10-13 20:11:55

这实际上是 Groovy 的安全取消引用运算符。您不能在纯 Java 中使用它(遗憾的是),因此该帖子完全是错误的(或者更可能有点误导,如果它声称 Groovy 是“Java 的最新版本”)。

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").

回首观望 2024-10-13 20:11:55

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

结果是 Java 7 考虑了此功能,但没有包含在内。

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.

所有深爱都是秘密 2024-10-13 20:11:55

正如很多答案提到的,Java 语言没有这个功能。

在少数情况下,使用这个编译器插件是可能的,有一些限制

在您提到的示例代码中可以是写为

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

PS:我是插件的作者

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

淡淡绿茶香 2024-10-13 20:11:55

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

这是H-MAN 解决方案的变体< /a> 但它使用带有多个参数的重载方法来处理多个步骤,而不是捕获 NullPointerException。

即使我认为这个解决方案有点酷,我想我更喜欢 Helder Pereira 的 第二个,因为它不需要任何 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; }
}

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; }
}
纵山崖 2024-10-13 20:11:55

我不确定这是否有效;比如说,如果 person 引用为 null,运行时会用什么替换它?一个新人?这将要求 Person 具有您在本例中所期望的一些默认初始化。您可以避免空引用异常,但如果您没有计划这些类型的设置,您仍然会遇到不可预测的行为。

这 ?? C# 中的运算符最好称为“合并”运算符;您可以链接多个表达式,它将返回第一个不为空的表达式。不幸的是,Java 没有它。我认为您能做的最好的事情就是使用三元运算符执行空检查,并在链中的任何成员为空时评估整个表达式的替代方案:

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

您还可以使用 try-catch:

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

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 "";
}
故事灯 2024-10-13 20:11:55

现在您已经看到了 Java 8 中的 null 安全调用:

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

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;
    }
}
青萝楚歌 2024-10-13 20:11:55

如果有人正在寻找旧 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;
    }

}

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

}
金兰素衣 2024-10-13 20:11:55

你可以测试一下你提供的代码,它会给出语法错误。所以,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

Optional 示例代码

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

思念满溢 2024-10-13 20:11:55

步骤 1:使用此通用方法。

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 下面是使用此方法的一个示例

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

if(rs.isPresent()) {
                    
}

完成!

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 !!

很糊涂小朋友 2024-10-13 20:11:55

由于 Android 不支持 Lambda 函数,除非您安装的操作系统 >= 24,因此我们需要使用反射。

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

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;
        }
    }
}
﹏雨一样淡蓝的深情 2024-10-13 20:11:55

如果这对您来说不是性能问题,您可以编写

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

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;
  }
} 
云巢 2024-10-13 20:11:54

最初的想法来自groovy。它被提议作为 Java 7 项目 Coin 的一部分:https://wiki .openjdk.java.net/display/Coin/2009+Proposals+TOC(Elvis 和其他空安全运算符),但尚未被接受。

相关的 Elvis 运算符 ?: 被提议将 x ?: y 简写为 x != null ? x : y,当 x 是复杂表达式时特别有用。

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.

待天淡蓝洁白时 2024-10-13 20:11:54

Java 中不存在这种语法,据我所知,它也不会包含在任何即将推出的版本中。

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

記柔刀 2024-10-13 20:11:54

[编辑]

经过进一步思考,我发现实际上仅使用标准 Java 8 类就可以实现相同的目的:

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

在这种情况下,甚至可以选择默认值(例如 "<没有名字>"),通过将其作为 orElse 的参数传递来代替 null


[原始答案]

解决缺少“?”的一种方法使用 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();
}

[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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文