当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的行为……