java - 发生异常时如何定位lambda?

标签 java exception lambda

假设我有一个 ComparatorFactory,它有 许多 由 lambda 组成的比较器:

   public static Comparator<SomeClass> getXCmp() {
      return (o1, o2) -> {
         Double d1 = Double.parseDouble(o1.getX());
         Double d2 = Double.parseDouble(o2.getX());
         return d1.compareTo(d2);
      };
   }

我使用这些比较器对数据进行排序和过滤。 不幸的是,我在某些地方使用了错误的比较器,它导致了 ClassCastException,如下所示:

java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
at businesslogic.utility.ComparatorFactory$$Lambda$24/115669291.compare(Unknown Source)
at javax.swing.DefaultRowSorter.compare(DefaultRowSorter.java:968)
...
...

如您所见,它显示了 (Unknown Source),这让我很难找出哪个比较器出错了。我还尝试在比较发生之前添加一个断点(即,在上面的示例中,在 DefaulRowSorter.java:968),但是 next step 也找不到哪个 lambda它是(它跳转到与 doublestring 无关的错误比较器,当我终于找到错误时,它不是正确的)。

在我发现这个 bug 之后(通过尝试理解整个项目和很多时间),我尝试了一个匿名类。堆栈的回溯明确告诉我它在哪里。

问:

如果我希望 lambda 提供简洁的代码,有没有什么好的方法可以找到 lambda 源在哪里,或者有什么好的做法可以在发生异常时帮助我?

A simple example重现类似的问题。

最佳答案

确保在编译类时为 javac 包含此选项:

-g:lines,source,vars

“-g”编译器选项可用于控制应在类文件中生成多少调试信息(参见 documentation)

这是一个使用 lambda 的简单示例:

package test;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class TestLambda {

    public static Comparator<String> comparator1() {
        return (o1, o2) -> {
            return o1.compareTo(o2);
        };
    }

    public static Comparator<String> comparator2() {
        return (o1, o2) -> {
            System.out.println("test");
            if (true) {
                throw new RuntimeException("Exception"); // line 20: stacktrace points to this line
            }
            return o1.compareTo(o2);
        };
    }

    public static void main(String[] args) {
        List<String> strings = Arrays.asList("string1", "string2", "string3");

        Collections.sort(strings, comparator2());
    }
}

这是堆栈跟踪:

Exception in thread "main" java.lang.RuntimeException: Exception
    at test.TestLambda.lambda$comparator2$1(TestLambda.java:20)
    at test.TestLambda$$Lambda$1/189568618.compare(Unknown Source)
    at java.util.TimSort.countRunAndMakeAscending(TimSort.java:351)
    at java.util.TimSort.sort(TimSort.java:216)
    at java.util.Arrays.sort(Arrays.java:1438)
    at java.util.Arrays$ArrayList.sort(Arrays.java:3895)
    at java.util.Collections.sort(Collections.java:175)
    at test.TestLambda.main(TestLambda.java:29)

如您所见,堆栈跟踪 at test.TestLambda.lambda$comparator2$1(TestLambda.java:20) 指向源代码的确切行。

您的 IDE 应该能够解析堆栈跟踪并使用链接对其进行修饰,单击这些链接应该会将您带到源代码中的确切行(至少 IntelliJ IDEA 是这样做的)。

如果您使用 -g:none 进行编译,堆栈跟踪将有所不同:

Exception in thread "main" java.lang.RuntimeException: Exception
    at test.TestLambda.lambda$comparator2$1(Unknown Source)
    at test.TestLambda$$Lambda$1/189568618.compare(Unknown Source)
    at java.util.TimSort.countRunAndMakeAscending(TimSort.java:351)
    at java.util.TimSort.sort(TimSort.java:216)
    at java.util.Arrays.sort(Arrays.java:1438)
    at java.util.Arrays$ArrayList.sort(Arrays.java:3895)
    at java.util.Collections.sort(Collections.java:175)
    at test.TestLambda.main(Unknown Source)

更新:

下面是另一个更接近问题中提供的例子:

package test;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class TestLambda {

    public static Comparator<String> comparator1() {
        return (o1, o2) -> {
            return o1.compareTo(o2);
        };
    }

    public static Comparator<String> comparator2() {
        return (o1, o2) -> {
            System.out.println("test");
            if (true) {
                throw new RuntimeException("Exception");
            }
            return o1.compareTo(o2);
        };
    }

    public static void main(String[] args) {
        List strings = Arrays.asList(1, 2, 3);

        Collections.sort(strings, comparator2());
    }
}

唯一的区别是它使用 List 的原始类型,因此可以将 String 比较器用于 Integers 列表。堆栈跟踪确实不包含行号,因为异常发生在转换期间而不是在我们的源代码中:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at test.TestLambda$$Lambda$1/189568618.compare(Unknown Source)
    at java.util.TimSort.countRunAndMakeAscending(TimSort.java:351)
    at java.util.TimSort.sort(TimSort.java:216)
    at java.util.Arrays.sort(Arrays.java:1438)
    at java.util.Arrays$ArrayList.sort(Arrays.java:3895)
    at java.util.Collections.sort(Collections.java:175)
    at test.TestLambda.main(TestLambda.java:29)

这里的经验法则是不要使用原始类型,在这种情况下这会使调试过程更容易 (What is a raw type and why shouldn't we use it?)。编译器也可以在这里帮助您:为 javac 包含此选项:

-Xlint:all

编译器会警告你关于原始类型的很多其他事情。添加另一个选项:

-Werror

并且编译器将产生错误而不是警告(在与 CI 服务器一起使用以确保源代码的高质量时很有用)

关于java - 发生异常时如何定位lambda?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30424735/

相关文章:

java - 代码内部发生 NoSuchElementException

java - 混合两个通用列表java

java - Jenv 未拾取系统安装的 java

exception - Groovy catch 语句奇怪的行为

c# - 查找 C# 异常处理程序

Python - 使用 'import signal' 处理 CTRL+D

ruby - 在 Lisp/Ruby 中将 IF-ELSE 实现为过程

java - Spring Batch 中的多线程

c# - 如何正确地将 lambda 表达式的 "var"结果转换为具体类型?

c++ - std::transform 以两个 std::vector 和一个常量作为参数