在Java中无需空检查就可以获取值


15

很多时候,我发现自己从某些数据层次结构中获取值时会进行空检查,以避免NullPointerExceptions,因为我发现NullPointerExceptions容易出错并且需要大量样板。

我编写了一个非常简单的例程,使我在获取对象时可以跳过空检查...

public final class NoNPE {

    public static <T> T get(NoNPEInterface<T> in) {
        try {
            return in.get();
        } catch (NullPointerException e) {
            return null;
        }
    }

    public interface NoNPEInterface<T> {
        T get();
    }
}

我有点像这样

Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());

上面的结果导致我得到一个Room对象或一个null,而不必对所有父级进行null检查。

您如何看待以上内容?我在创建有问题的模式吗?您认为有更好的方法吗?


1
当您显然使用Java 8时,我是否建议您考虑重新设计应用程序以使用java.util.Optional空值代替空值来表示丢失的数据?这既提供的情况下,你描述方便的工具,并在您想使用默认的数据进行的情况下,而不是仅仅在链的端返回的故障情况..
Periata Breatta

我认为您基本上已经重新发现了Option(或Maybe)monad :)
Andres F.

可能返回Optional而不是T或null:这样,您可以直接使用orElse()方法。18个月后,但可以帮助某人。

这篇文章中提到了另一种方法invalidargumentexception.blogspot.com/2015/03/…,其中一种方法是使用名为kludje的库,该库具有非常有趣的语法
Benj,

Answers:


13

您的解决方案非常聪明。我看到的问题是您不知道为什么会得到一个null?是因为房子没有房间吗?是因为该镇没有房屋吗?是因为该国没有城镇吗?是因为null即使在位置1或更大的位置上存在房屋,也会由于错误而在集合的0位置存在a ?

如果您扩展使用NonPE该类,则将遇到严重的调试问题。我认为,最好是知道链条到底在哪里断了,而不是默默地获取null可能隐藏着更深层错误的信息。

这也违反了Demeter法则country.getTown().getHouses().get(0).getLivingRoom()。通常,违反某些良好原则会使您不得不实施非传统的解决方案来解决因违反此类原则而导致的问题。

我的建议是谨慎使用它,并尝试解决设计缺陷,该缺陷使您不得不在火车残骸反模式中使用(因此您不必NonPE在任何地方都使用)。否则,您可能会遇到难以检测的错误。


好答案。是的,我不知道在链中哪里有空值。在许多情况下,尽管我不在乎,也不必进行null检查,这意味着代码更具可读性,并且不易出现样板错误。但是,是的,在某些情况下您是对的,如果父对象为null,则我确实需要做出不同的逻辑决定,那么这会引起问题。常规方法或Optional类可能是一个更安全的解决方案。
Eurig Jones

通常,在使用Optionmonad时,您无需担心缺失值在链中的哪个位置。当您真正关心它时,您可能会使用其他类型,例如Either
安德列斯F.

OP的方法类似于C#6 ?.?[]运算符。当您可能想使用这种东西时,一个示例是分层服务器端设置。var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;谁在乎为什么其中任何部分都不为空?也许您无法获取设置。也许您决定删除部分设置。无论如何,您永远都不能指望获得服务器的设置,因此默认设置通常是个好主意,而且您不太在乎为什么无法获得实际的设置,除非它经常在不该设置的情况下发生。
克里斯

现在,我并不是说这比对默认值进行本地化并仅通过常规访问获取所需的值要好还是坏settings.a.b.c。再说一次,这是一个孤立的例子。
克里斯

10

这个想法很好,实际上很好。由于Optional存在Java 8 类型,因此可以在Java Optional type中找到详细说明。您发布的示例是

Optional.ofNullable(country)
    .map(Country::getTown)
    .map(Town::Houses);

并进一步。


1
是的,我知道Java 8和Guava中的Optional类,它们确实很有用。但是您不能像通常那样使代码更难阅读且性能也稍差一些那样获取对象。但好处是Optional类提供了许多非常有用的运算符。
欧里格·琼斯

3
@EurigJones我不认为代码的性能会下降。在旁观者的眼中,可读性很好,但是我认为这Optional是两者之间更具可读性的解决方案,仅仅是因为-与您的建议不同-这是一个非常普遍的习惯用法。比您的简明扼要!
安德列斯F.

0

您的方法可以很好地实现其预期目的,尽管null在听到NullPointerException听起来像不良设计的声音时返回s 。

null如果可以,请尽量避免使用s,并且仅在它们表示某物或具有特殊含义时才传递它们,并且仅在它们表示/意味着某物时才返回它们-否则应抛出NullPointerException。这样可以避免错误和混乱。如果Object不应该null,则NullPointer应该抛出。如果一个对象可以是一个对象,null那么当传入一个对象时,不会有任何问题。否则,上述方法将起作用。


0

我可以感觉到您的痛苦,但是建议的解决方案不是一个好主意。

  • 如果一个吸气剂由于其他原因抛出NPE,您将忽略它。
  • 该内部lambda可能会增长为可怕的代码。例如,如果有新的要求在城镇中没有房屋时返回一个特殊的常量,那么懒惰的程序员可以扩展lamda,将所有内容都包裹在内NoNPE.get
  • 如前所述,Optional.map您正在寻找什么。
  • 创建新的NullPointerException实例的代价通常是很大的。这是许多微秒,特别是随着您的调用堆栈越来越大。很难预测您的实用程序最终将在哪里使用。

作为旁注,NoNPEInterface是的重复项java.util.function.Supplier

在某些情况下,您可能会考虑使用许多框架中都存在的表达式评估工具(例如:EL,SpEL):

evaluateProperty(country, "town.houses[0].livingRoom")

网页模板还可以,但是通常开发速度很慢(不检查编译时间)并且运行速度很慢。
凯文·克莱恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.