如何处理从方法返回的null



我有一个与图相关的方法,它返回某个节点的相邻节点。如果一个节点没有邻居,则返回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。如何解决这个问题?

如果您仍然得到一个NPE,那么问题出在getNeighbours中,而不是第二个代码段中。

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

假设您将一个名称传递给一个方法,然后该方法将通过节点进行查找,并且您不能在列表中调用.get(someNodeRef),那么adjacencyList可能是某种哈希图,因此您的名称是关闭的,您应该重命名一些东西。如果没有找到条目,Map的.get(x)方法将返回null,因此最有可能的罪魁祸首是v根本不在映射中,因此.get(v).isEmpty()抛出NPE。

修复如下:

  1. 当具有预期语义的有效sentinel值可用时,您应该NEVER返回null。一口,但它的意思是:当你打算以与"零节点"完全相同的方式处理时,为什么要返回null?Iterable<Node>的一个实例正确地表示了零节点的概念,但它不是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,而某些sentinel值(如空白字符串或空列表)以同样的方式处理,那么您的代码风格就不好了;然而,你得到了null应该得到的是空值。例如,如果你写过这样的话:

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

你搞砸了。弄清楚你从哪里得到x。在那里更新它,使x为空白sentinel(""表示字符串,List.of表示列表,等等)。

并且更多地使用.getOrDefault和其他这样的方法:让您提供当例如找不到密钥时应该发生的事情的方法。

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

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

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

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

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

在您的情况下,这将应用如下(注意,这是假设adjancencyList是一个非null对象,并且它的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),从而迫使其相应地采取行动。

相关内容

  • 没有找到相关文章

最新更新