有时命名lambdas很有用。特别是当你将它们作为参数传递时。
一个简单的例子是
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而不会失去幕后的优秀性能优化。
这样的想法对我来说没问题:
Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty());
答案 0 :(得分:1)
这是另一种想到的选择:
static <T> Predicate<T> nameIt(String name, Predicate<? super T> pred) {
return new Predicate<T>() {
public String toString() { return name; }
public boolean test(T t) { return pred.test(t); }
};
}
这看起来很简单。虽然我还没有对它进行基准测试,但它看起来应该很快。它添加了一个对象和一个方法调用,它避免了装箱/拆箱开销。
缺点是您必须为要为其提供命名实例的每个功能接口编写类似这样的函数。
答案 1 :(得分:0)
这是我的解决方案(灵感来自于https://stackoverflow.com/a/23705160/1325574的andersschuller解决方案)。可能有一些极端情况(Classloading),这种实现不起作用,但对于最简单的情况,它可以工作。
我用我有限的jmh知识创建了一个小的性能测试: https://gist.github.com/picpromusic/4b19c718bec5a652731a65c7720ac5f8
&#34;命名&#34; - 结果是为了实现@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是愚蠢的。
以下是代码:
/**
* 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;
}
}
}
你有其他解决方案吗?也许某些东西没有性能影响?