Fed*_*zza 104 java debugging lambda java-8
在我们的项目中,我们正在迁移到Java 8,我们正在测试它的新功能.
在我的项目中,我使用Guava谓词和函数来使用Collections2.transform和过滤和转换一些集合Collections2.filter.
在这次迁移中,我需要将例如guava代码更改为java 8更改.所以,我正在做的改变是这样的:
List<Integer> naturals = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10,11,12,13);
Function <Integer, Integer> duplicate = new Function<Integer, Integer>(){
@Override
public Integer apply(Integer n)
{
return n * 2;
}
};
Collection result = Collections2.transform(naturals, duplicate);
Run Code Online (Sandbox Code Playgroud)
至...
List<Integer> result2 = naturals.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
使用guava我调试代码非常容易,因为我可以调试每个转换过程,但我关心的是如何调试例如.map(n -> n*2).
使用调试器我可以看到一些代码,如:
@Hidden
@DontInline
/** Interpretively invoke this form on the given arguments. */
Object interpretWithArguments(Object... argumentValues) throws Throwable {
if (TRACE_INTERPRETER)
return interpretWithArgumentsTracing(argumentValues);
checkInvocationCounter();
assert(arityCheck(argumentValues));
Object[] values = Arrays.copyOf(argumentValues, names.length);
for (int i = argumentValues.length; i < values.length; i++) {
values[i] = interpretName(names[i], values);
}
return (result < 0) ? null : values[result];
}
Run Code Online (Sandbox Code Playgroud)
但它没有像Guava那样直接调试代码,实际上我找不到n * 2转换.
有没有办法看到这种转换或轻松调试此代码的方法?
编辑:我添加了不同评论的答案并发布了答案
感谢Holger回答我的问题的评论,使用lambda块的方法让我看到了转换过程并调试了lambda体内发生的事情:
.map(
n -> {
Integer nr = n * 2;
return nr;
}
)
Run Code Online (Sandbox Code Playgroud)
感谢Stuart Marks方法引用的方法也允许我调试转换过程:
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Run Code Online (Sandbox Code Playgroud)
感谢Marlon Bernardes回答我注意到我的Eclipse没有显示它应该是什么,peek()的使用有助于显示结果.
Mar*_*des 77
在使用Eclipse Kepler或Intellij IDEA(使用JDK8u5)时,我通常没有问题调试lambda表达式.只需设置断点并确保不检查整个lambda表达式(仅检查lambda体).

另一种方法是用来peek检查流的元素:
List<Integer> naturals = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13);
naturals.stream()
.map(n -> n * 2)
.peek(System.out::println)
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
更新:
我认为你会感到困惑,因为map它是intermediate operation- 换句话说:它是一个懒惰的操作,只有在执行后才会terminal operation执行.所以当你调用stream.map(n -> n * 2)lambda体时此刻并没有被执行.您需要设置断点并在调用终端操作后检查它(collect在本例中).
检查流操作以获得进一步说明.
更新2:
引用霍尔格的评论:
让这里变得棘手的是,对map和lambda表达式的调用是在一行中,因此行断点将停止在两个完全不相关的动作上.
在之后插入换行符
map(将允许您仅为lambda表达式设置断点.并且调试器不显示return语句的中间值并不罕见.将lambda更改为n -> { int result=n * 2; return result; }允许您检查结果.再次,逐行踩踏时适当插入换行符......
Dmy*_*huk 29
IntelliJ在这种情况下有一个很好的插件作为Java Stream Debugger插件.我建议你看一下这个:https: //plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform = skipsuite
它通过添加Trace Current Stream Chain按钮扩展了IDEA调试器工具窗口,该按钮在调试器停止在Stream API调用链中时变为活动状态.
它具有用于处理单独流操作的良好界面,并使您有机会遵循应调试的某些值.

Stu*_*rks 22
调试lambda也适用于NetBeans.我正在使用NetBeans 8和JDK 8u5.
如果在存在lambda的行上设置断点,则实际上将在管道设置时点击一次,然后对每个流元素点击一次.使用您的示例,第一次点击断点将是map()设置流管道的调用:

您可以main按预期看到调用堆栈以及局部变量和参数值.如果继续步进,则再次点击"相同"断点,除非这次是在对lambda的调用中:

请注意,这次调用堆栈位于流机制的深处,局部变量是lambda本身的本地,而不是封闭main方法.(我已经更改了naturals列表中的值以使其清楚.)
正如Marlon Bernardes指出的那样(+1),你可以用它peek来检查管道中的值.如果你从并行流中使用它,请小心.可以跨不同线程以不可预测的顺序打印值.如果您将值存储在调试数据结构中peek,那么该数据结构当然必须是线程安全的.
最后,如果你正在对lambdas进行大量调试(特别是多行语句lambdas),最好将lambda提取到一个命名方法中,然后使用方法引用引用它.例如,
static int timesTwo(int n) {
return n * 2;
}
public static void main(String[] args) {
List<Integer> naturals = Arrays.asList(3247,92837,123);
List<Integer> result =
naturals.stream()
.map(DebugLambda::timesTwo)
.collect(toList());
}
Run Code Online (Sandbox Code Playgroud)
这可能会让您在调试时更容易看到正在发生的事情.此外,以这种方式提取方法使单元测试更容易.如果你的lambda非常复杂,你需要单步执行它,那么你可能想要为它进行一系列的单元测试.
只是为了提供更多更新的细节(2019 年 10 月),IntelliJ 添加了一个非常好的集成来调试此类非常有用的代码。
当我们在包含 lambda 的行处停止时,如果我们按下F7(step into),那么 IntelliJ 将突出显示要调试的代码段。我们可以切换要调试的块Tab,一旦我们决定它,然后我们F7再次单击。
这里有一些截图来说明:
Intellij IDEA 15似乎让它更容易,它允许停在lambda所在的行的一部分,请参阅第一个功能:http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -EAP-是开/