【问题标题】:Null Pointer Exception despite Optional<>尽管 Optional<> 出现空指针异常
【发布时间】:2018-08-25 13:55:25
【问题描述】:

我有:

List<Optional<MyObject>> myList;

此列表是通过从文件中读取来填充的。文件读取完成后,我会检查空列表: if(myList.size() == 0){//} 然后进行如下操作:

myList.stream()
    .filter(Optional::isPresent)
    .map(i → i.orElse(new MyObject(“adventureBook”, 20))
    .collect(groupingBy(MyObject::getBookType, TreeMap::new, mapping(MyObject::getBookPrice, toList())));

我有大约 350k MyObject 文件要读取,300k 文件可以正常读取,但是当我尝试读取整批 c.350k 文件时,它会在 collect() 上引发空指针异常。

怎么可能尽管包装在 Optional 并检查 Optional::isPresent、Optional::orElse 等仍然有一个空对象设法偷偷溜过,并且考虑到我有这么多的文件,最好的方法是什么尝试缩小错误文件的范围?谢谢

编辑:添加堆栈跟踪

Exception in thread "main" java.lang.NullPointerException
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.mypackage.MyObject.main(MyObject.java:108)

【问题讨论】:

  • 你能发布堆栈跟踪吗?
  • 拥有一个可选项列表已经很奇怪了,不存在的对象一开始就不应该出现在列表中。
  • 即使有一个MyObject 为空,将那些20(s) 收集到List 也是一个相当有趣的选择:) 如果有一本真正的书怎么办(adventureBook ) 价格为20?你将如何在两者之间做出改变?
  • 如果您将null 添加到该列表而不是Optional.empty(),则Optional 不会神奇地保护您免受NullPointerException 的侵害。

标签: java optional


【解决方案1】:

可能是MyObject中的非空属性之一是空的?您确实在做filter(Optional::isPresent),但这并不意味着这些字段本身不为空。 MyObject::getBookTypeMyObject::getBookPrice 很容易仍然为空。

【讨论】:

  • 感谢所有回复的人。考虑到 cmets,我在将文件读取参数传递给 MyObject 构造函数之前进行了进一步的 null 检查,但问题仍然存在,堆栈跟踪对于 cmets 来说太长了,我正在将其编辑到 OP。
  • @shanlodh MyObject 的第 108 行是什么?
  • 是OP中提到的.collect()
  • 我提到的 350k 文件分布在 c.15k 文件夹中,并且在一些文件夹中似乎有一些非 .gz2 文件让 org.apache.commons.compress.compressors 感到不安.* 我正在使用的反序列化器。使用实现 FileNameFilter 的辅助类解决了这个问题。再次感谢大家
  • @shanlodh 你明白这实际上与问题本身无关,对吧?好想你解决了,我下次提供所需的所有细节
【解决方案2】:

关于“尝试缩小错误文件范围的最佳方法是什么?”这个问题,我通常在解析时使用这样的模式:

for (File file : filesToParse) {
    try {
        parseFile(file);
    } catch (IOException e) {
        // rethrow the exception, and make sure that
        // 1. you give the file in the message, to help narrow the error down
        // 2. pass the original exception as 2nd parameter, to preserve the stack trace
        throw new IOException("Failed to parse file: " + file, e);
    }
}

【讨论】:

  • 您不应捕获所有运行时异常,只捕获您感兴趣的异常,例如IOException。捕获所有内容只能作为您的 main 应用程序周围的 super wrapper 完成,并且仅用于日志记录。
  • 我的理解是无论如何都不应该捕获特定的运行时异常,所以我认为抛出不同的运行时异常没有问题,有更多的细节来帮助调试。例如,请参阅stackoverflow.com/questions/45607947IOException 不是运行时异常,因此如果抛出,则需要以不同方式处理。
  • @Zabuza 您建议的解决方案是什么?
  • 只捕获IOException。让其余的通过。而且,如果您真的想捕获所有内容,则还需要包含错误。所以Throwable 而不是RuntimeException。尽管是否应该在错误中搭载RuntimeException(例如IllegalStateException)值得怀疑。
  • @Zabuza 喜欢这样吗?
猜你喜欢
  • 2015-07-25
  • 2013-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-31
  • 2013-10-29
相关资源
最近更新 更多