使用带有Java 8流的交互式调试器的问题

spr*_*ter 9 java debugging java-8 java-stream

我喜欢 Java 8流.它们直观,强大而优雅.但它们确实有一个主要的缺点IMO:它们使调试更加困难(除非你可以通过调试lambda表达式来解决你的问题,这在这里得到解答).

考虑以下两个等效片段:

int smallElementBitCount = intList.stream()
    .filter(n -> n < 50)
    .mapToInt(Integer::bitCount)
    .sum();
Run Code Online (Sandbox Code Playgroud)

和

int smallElementBitCount = 0; 
for (int n: intList) {
    if (n < 50) {
        smallElementBitCount += Integer.bitCount(n);
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现第一个更清晰,更简洁.但是请考虑结果不符合您预期的情况.你是做什么?

在传统的迭代样式中,您totalBitCount += Integer.bitCount(n);在行上放置一个断点并逐步浏览列表中的每个值.您可以看到当前列表元素是什么(监视n),当前总计(监视totalBitCount),以及根据调试器,Integer.bitCount的返回值是什么.

在新的流式中,所有这一切都是不可能的.您可以在整个语句中放置断点并逐步执行该sum方法.但总的来说这是无用的.在我测试的这种情况下,我的调用堆栈是11深,其中10个是我不感兴趣的java.util方法.不可能单步执行代码测试谓词或执行映射.

在调试流问题的答案中注意到,迭代调试器可以很好地破坏内部lambda表达式(例如n < 50谓词).但在许多情况下,最合适的断点不在lambda中.

显然,这是一段简单的调试代码.但是,一旦添加了自定义缩减和集合,或者更复杂的过滤器和映射链,它就会成为调试的噩梦.

我在NetBeans和Eclipse上试过这个,但两者似乎都有同样的问题.

在过去的几个月里,我习惯于使用.peek调用来记录临时值或将临时步骤移动到他们自己的命名方法中,或者在极端情况下,重构为迭代,直到任何错误被整理出来.这可以工作,但它让我想起了在使用集成交互式调试器的现代IDE之前的许多糟糕的旧时代,当你不得不通过代码分散printf语句时.

当然有更好的方法.

具体来说,我想知道:

  • 还有其他人遇到同样的问题吗?
  • 是否有"流感知"交互式调试器可用?
  • 是否有更好的技术来调试这种代码?
  • 这是将流的使用限制为简单案例的原因吗?

您发现成功的任何技术都将非常感激.

Wal*_*rry 2

我不完全确定是否有可行的解决方案来解决这个问题。据我所知,通过使用流,您可以有效地将迭代(以及相关代码)委托给虚拟机,从而将流程推入一个黑匣子,即流本身。

至少从我读到的有关它们的内容来看是这样。对我来说,这就是 lambda 代码周围发生的事情(如果它们足够复杂,则很难跟踪它们周围发生的事情)。我对任何调试选项都很感兴趣,但我个人还没有找到。