用于调试目的的命名(toString)Lambda 表达式
有时命名 lambdas 很有用.特别是当您将它们作为参数传递时.
Sometimes it is usefull to name lambdas. Especially when you pass them around as parameter.
一个非常简单的例子是
public class Main {
public static void main(String[] args) {
Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty());
maybePrint("Hello", p);
maybePrint(" ", p);
}
static <T> void maybePrint(T s, Predicate<T> pred) {
if (pred.test(s)) {
System.out.println(s.toString());
} else {
System.err.println(pred + " says no to "" + s + """);
}
}
}
如果 jvm 有一些功能来命名 lambdas 而不会失去幕后出色的性能优化,那就太好了.
It would be nice to have some functionality by the jvm to name lambdas without loosing the great performance optimizations behind the scenes.
这样的想法对我来说很好:
Somethink like this would be fine for me:
Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty());
推荐答案
这是我的解决方案(灵感来自 https 上的 andersschuller 的解决方案://stackoverflow.com/a/23705160/1325574) 的问题.可能有一些极端情况(类加载),这个实现不起作用,但对于最简单的情况,它可以工作.
This is my solution(inspired from the solution of andersschuller at https://stackoverflow.com/a/23705160/1325574) for the problem. There maybe some corner cases(Classloading) where this implementation does not work, but for the most simple cases it works.
我用我有限的 jmh 知识为此创建了一个小型性能测试:https://gist.github.com/picpromusic/4b19c718bec5a652731a65c7720ac5f8
I have created a small performance test of this with my limited jmh knowledge: https://gist.github.com/picpromusic/4b19c718bec5a652731a65c7720ac5f8
命名"结果是针对@stuartmarks 用于调试目的的命名(toString)Lambda 表达式
The "Named"-results are measured for the implementation of the answer of @stuartmarks Naming(toString) Lambda-Expressions for Debugging purpose
# Run complete. Total time: 00:40:31
Benchmark Mode Cnt Score Error Units
MyBenchmark.testNamedPredicate thrpt 200 45938970,625 ± 615390,483 ops/s
MyBenchmark.testPredicate thrpt 200 23062083,641 ± 154933,675 ops/s
MyBenchmark.testPredicateReal thrpt 200 48308347,165 ± 395810,356 ops/s
MyBenchmark.testToString thrpt 200 138366708,182 ± 1177786,195 ops/s
MyBenchmark.testToStringNamed thrpt 200 252872229,907 ± 8044289,516 ops/s
MyBenchmark.testToStringReal thrpt 200 6670148,202 ± 40200,984 ops/s
如您所见,它比使用未命名的 lambda 慢大约 2 倍.所以在设置 -DnamedLambdasEnabled=true 时要小心.对我来说有趣的是,在 Real-lambda 上调用 toString 非常昂贵.也许有人可以解释一下,或者我的 jmh-test 很愚蠢.
As you can see it is roughly 2 times slower than using an unnamed lambda. So be carefull in setting -DnamedLambdasEnabled=true. Interessting for me is that it is surprisingly expensive to call toString on an Real-lambda. Maybe someone can explain that, or my jmh-test is stupid.
代码如下:
/**
* Helper Class to give lambda a name ("toString") for debugging purpose
*
*/
public class LambdaNamer {
private static Method TO_STRING;
static {
try {
TO_STRING = Object.class.getMethod("toString");
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException("There is something rotten in state of denmark!");
}
}
/**
* Overrides toString "Method" for a given lambda.
*
* @param name toString result of lambda
* @param obj the lambda to encapsulate
* @return the named lambda
*/
public static <T> T nameIt(String name, T obj) {
if (Boolean.getBoolean("namedLambdasEnabled")) {
Class<T> clazz = (Class<T>) obj.getClass();
Class<?>[] interfaces = clazz.getInterfaces();
return (T) Proxy.newProxyInstance(//
obj.getClass().getClassLoader(),//
interfaces, //
(Object proxy, Method method, Object[] args) -> {
if (TO_STRING.equals(method)) {
return name;
} else {
return method.invoke(obj, args);
}
});
} else {
return obj;
}
}
}
您还有其他解决方案吗?也许一些对性能没有影响的东西?
Do you have other solutions? Maybe something that does not have performance implications?
相关文章