【问题标题】:How to handle a null returned from a method?如何处理从方法返回的空值?
【发布时间】:2021-05-01 09:10:42
【问题描述】:

我有一个与图相关的方法,它返回某个节点的相邻节点。 如果一个节点没有邻居则返回null,方法如下

public Iterable<Node> getNeighbors(Node v) {
    if (!this.adjacencyList.get(v).isEmpty())
        return this.adjacencyList.get(v);
    return null;
}

我尝试使用以下方法避免异常:

if (graph.getNeighbors(nodeIterator.name) == null)
            nodeIterator = all_graph_nodes.iterator().next();
Iterable<Node> adjNodes = graph.getNeighbors(nodeIterator.name);

NullPointerException 即使使用之前的代码也会引发。 如何解决?

【问题讨论】:

  • 请提供 NullPointerException 堆栈跟踪

标签: java nullpointerexception null


【解决方案1】:

您应该避免从 getNeighbors 方法返回 null。为 Iterables、Iterators 和 Collections 返回 null 不是一个好习惯,因为一个空的 iterable 将代表相同的概念(该邻接列表中没有任何内容)而没有 null 的所有危险。你的代码会更简单。您可以检查可迭代对象是否包含任何内容,如果不包含则默认为完整的迭代器。

【讨论】:

  • 我尝试返回一个空集,但同样的错误
  • 那么请提供异常堆栈跟踪
  • 堆栈没有行,只是提到了异常,除了 if 语句发生错误外,您应该知道的更多信息,但是您的逻辑不起作用,它是错误的并且产生相同我的代码错误抱歉
  • 嗯,错误总是会告诉你哪一行出错了,等等。告诉那些试图帮助你的人他们错了而不给他们要求的信息不会给你太多帮助.请提供不返回 null 的代码并告诉我们您在哪一行得到 NullPointerException。
【解决方案2】:

如果您仍然获得 NPE,那么问题出在 getNeighbours 而不是第二个 sn-p。

  1. this.adjacencyList 为空,-或-
  2. this.adjacencyList.get(v) 返回 null。

假设您将 name 传递给一个方法,然后该方法将通过 node 进行查找,并且您不能在列表中调用 .get(someNodeRef) , adjacencyList 可能是某种哈希图,所以你的名字是关闭的,你应该重命名一些东西。如果找不到条目,​​Map 的 .get(x) 方法会返回 null,因此很可能罪魁祸首是 v 根本不在地图中,因此 .get(v).isEmpty() 会抛出 NPE。

修复如下:

  1. 您应该从不在带有预期语义含义的有效标记值可用时返回 null。拗口,但这意味着:当您打算以与“零节点”完全相同的方式对待它时,为什么要返回 null ?有一个Iterable&lt;Node&gt; 的实例正确地代表了零节点的概念,它不是null。它是List.of() 或等价物:空列表没有节点。伟大的。这就是你的意图。所以返回那个。

  2. .get(v).isEmpty() 在这里是错误的代码,因为如果您要求一个不存在的节点,这意味着会发生 NPE。当然,除非您希望它以这种方式工作。一个简单的出路是默认机制:改为调用.getOrDefault

if (!this.adjacencyList.getOrDefault(v, List.of()).isEmpty()) ....

当然,当您可以返回一个空列表时,您永远不应该返回 null,因此您的 getNeighbours 方法变得简单:

return adjacencyMap.getOrDefault(v, List.of());

单线可以解决所有问题。

一般来说,如果您正在编写代码,其中null 以某种方式处理,并且某些标记值(例如空字符串或空列表)以相同方式处理,则您的代码样式错误;但是你知道null 应该让你得到那个空值。例如如果你写过这个:

if (x == null || x.isEmpty()) ...

你搞砸了。弄清楚你从哪里得到 x。在那里更新它,将 x 设为空白标记("" 用于字符串,List.of 用于列表等)。

那,并使用.getOrDefault 和其他类似的方法更多:让你提供什么时候应该发生的方法,例如未找到密钥。

【讨论】:

    【解决方案3】:

    您应该不惜一切代价避免返回null。这是非常危险的,因为它可能会导致在运行时抛出空指针异常。此类异常很难调试,因为它们通常隐藏实现错误,因为抛出异常的位置很可能远离原始实现错误。 您的案例实际上是此类行为的一个很好的例子,因为它无法直接理解 NPE 的来源。

    null 值的出现不可避免的情况下(例如@rzwitserloot 指出,Java 的Map get 方法)并且有可能将其暴露给客户端对象(例如您的@987654326 @ 方法可能会暴露这样的 null 值)我喜欢使用 Java 的 Optional (如文档中所述)是:

    一个容器对象,它可能包含也可能不包含非空值。如果存在值,isPresent() 将返回 true,而 get() 将返回该值。

    此对象将充当可能被分配为null 的对象的包装器,从而防止直接使用它并可能防止抛出 NPE。

    在您的情况下,这将适用如下(请注意,这是假设 adjancencyList 是一个非空对象,并且它的 get 方法是实际抛出 NPE 的方法):

    public Optional<Iterable<Node>> getNeighbors(Node v) {
        return Optional.ofNullable(this.adjacencyList.get(v));
    }
    
    if (!graph.getNeighbors(nodeIterator.name).isPresent()) {
        nodeIterator = all_graph_nodes.iterator().next();
    }
    
    Iterable<Node> adjNodes = graph.getNeighbors(nodeIterator.name).get();
    

    请注意,通过将原始get 方法包装在Optional 对象中,不再传播原始null 值,从而阻止它被客户端使用。您只是将处理null 的责任转移到您身边,并保护客户处理它们。

    使用Optional 作为方法的返回类型的另一个巨大优势是它隐式声明该方法的返回对象可能存在也可能不存在。这迫使客户了解其返回值可能为空 (null),从而迫使其采取相应措施。

    【讨论】:

    • thx 但这条线Iterable&lt;Node&gt; adjNodes = graph.getNeighbors(nodeIterator.name); 不起作用,因为 getNeighbors 返回 Optional
    • 你打算使用 ifPresent() 吗?
    • 抱歉,我忘了包含 Optionalget() 方法,它检索实际的包装对象:Iterable&lt;Node&gt; adjNodes = graph.getNeighbors(nodeIterator.name).get();
    • 不起作用,它提供的对象不是 IDE 所说的可迭代对象
    • 您能否提供更多关于它为什么不起作用的详细信息?我刚刚在本地测试了它,它正在工作。
    猜你喜欢
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    相关资源
    最近更新 更多