当finalizeOperation运行时(在较大应用程序中的生产环境中):

public interface OperationFinalizerHook {
    void onOperationFinalize(Operation operation, Object context);
}
private final List<OperationFinalizerHook> operationFinalizeHooks = new ArrayList<>();
...
public void finalizeOperation(Object context) {
    final Operation operation = getOperation();
    operationFinalizeHooks.forEach(hook -> hook.onOperationFinalize(operation, context));
}

以下调用树/ stacktrace已构建:
11 at com.company.SomeClass.lambda$finalizeOperation$0 (SomeClass.java:51)
12 at com.company.SomeClass$$Lambda$135/2085968933.accept (Unknown source)
13 at java.util.ArrayList.forEach (ArrayList.java:1249)
14 at com.company.SomeClass.finalizeOperation (SomeClass.java:51)

我对第12行感兴趣-这个名字从哪里来?为什么在我期望 class 名称的地方会有随机数?

编辑:
这是blog post mentioned by Niklas P的代码:
public class Test {
    public static void main(String... args) {
        List<String> names = Arrays.asList("adam", "");
        Stream lengths = names.stream().map(name -> check(name));
        lengths.count();
    }
    public static int check(String s) {
        if (s.equals(""))
            throw new IllegalArgumentException();
        return s.length();
    }
}

但是结果不包含此数字名称,堆栈跟踪为(jdk8u102):
Exception in thread "main" java.lang.IllegalArgumentException
    at Test.check(Test.java:19)
    at Test.lambda$main$0(Test.java:12)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    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 Test.main(Test.java:14)

在jdk8u25上有数字:
Exception in thread "main" java.lang.IllegalArgumentException
    at Test.check(Test.java:18)
    at Test.lambda$main$0(Test.java:11)
    at Test$$Lambda$1/1554547125.apply(Unknown Source)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at Test.main(Test.java:13)

最佳答案

这里有两个重叠的问题。首先,当您将lambda表达式转换为对象类型时,必须要有一些实现功能接口的东西—细节不是那么重要。您唯一需要了解的是,将会实现接口并调用lambda表达式或方法引用目标的代码。

当前的JRE实现生成匿名类,顾名思义,该匿名类不依赖于其唯一性。在类名之后打印的数字是此属性的产物。不管有没有数字,无论哪种方式,您都无法使用ClassLoader查找这些类。

在堆栈跟踪中具有合成工件对于Java来说并不是什么新鲜事物。使用内部类时会生成访问器方法,例如

import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

public class Test {
    public static void main(String... args) {
        List<String> names = Arrays.asList("adam", "");
        Stream lengths = names.stream().map(new Function<String, Integer>() {
            public Integer apply(String name) {
                return check(name);
            }
        });
        lengths.count();
    }
    private static int check(String s) {
        if (s.equals(""))
            throw new IllegalArgumentException();
        return s.length();
    }
}

Exception in thread "main" java.lang.IllegalArgumentException
    at Test.check(Test.java:17)
    at Test.access$000(Test.java:5)
    at Test$1.apply(Test.java:10)
    at Test$1.apply(Test.java:8)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    … (shortened it a bit)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at Test.main(Test.java:13)

请注意,堆栈跟踪中存在access$000,该代码未出现在源代码中;它的相关行号是没有意义的,只是外部类定义的开始。

现在,似乎堆栈跟踪生成发生了变化,以省略最新JRE版本中匿名类的合成成员。这也将影响反射调用的堆栈跟踪,例如使用MethodHandle实例。在大多数情况下,这可能被认为是有用的,但同时也暗示在某些情况下,调用方和被调用方之间可能不匹配,因为堆栈跟踪报告调用方调用了接口方法,但最终终止于其他地方,例如:
import java.util.*;
import java.util.stream.Stream;

public class Test {
    public static void main(String... args) {
        Stream.of("adam", "", null).filter("foo"::contains).count();
    }
}

将打印

Exception in thread "main" java.lang.NullPointerException
    at java.lang.String.contains(String.java:2133)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
…

其中ReferencePipeline.java:174包含对accept接口的Predicate方法的调用,但最终出现在contains类的String方法中。我们可以做到最大:
import java.util.*;
import java.util.stream.Stream;

public class Test {
    public static void main(String... args) {
        Stream.of("adam", "", null).filter(String::isEmpty).count();
    }
}

将在最新的JRE上生成以下内容:

Exception in thread "main" java.lang.NullPointerException
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    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.LongPipeline.reduce(LongPipeline.java:438)
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at Test.main(Test.java:6)

省略最终将在isEmpty实例上调用String的合成代码,由于ReferencePipeline.java:174仅包含对interface方法的调用,而interface实例不是null(这在该代码的更早部分已进行了检查),因此可能更加令人困惑。

请注意,这是动态的。在Java 9中,将有 StackWalker API,它将允许应用程序通过配置的隐藏/反射堆栈帧处理来生成自己的快照。一旦应用程序使用此API创建可预测的堆栈跟踪,即不再依赖于Throwable.getStackTrace()的特定行为,则可通过JVM选项或系统属性来配置throwables的行为……

09-30 14:54
查看更多