假设我有一个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和当我最终发现该错误时,它不是正确的错误)。
在发现错误之后(尝试了解整个项目并花费了很多时间),我尝试了一个匿名类。堆栈的回溯明确地告诉了我它在哪里。
问:
如果我想让lambda提供简洁的代码,是否有什么好的方法可以找到lambda的源代码,或者有什么好的惯例可以在发生异常时为我提供帮助?
一个simple example重现类似的问题。

最佳答案

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

-g:lines,source,vars

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

这是一个使用lambdas的简单示例:
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)

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

您的IDE应该能够解析stacktrace并用链接装饰它,单击链接可以将您带到源代码中的确切行(至少这是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());
    }
}

唯一的区别是,它对列表使用原始类型,因此可以将String比较器用于Integers列表。 stacktrace确实不包含行号,因为该异常发生在转换期间,并且不在我们的源代码中:
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服务器一起使用以确保高质量的源代码时很有用)

10-05 18:01
查看更多