命名(toString)Lambda-Expressions以进行调试

有时命名lambdas是有用的。 特别是当你将它们作为参数传递时。

一个非常简单的例子是

public class Main { public static void main(String[] args) { Predicate p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty()); maybePrint("Hello", p); maybePrint(" ", p); } static  void maybePrint(T s, Predicate pred) { if (pred.test(s)) { System.out.println(s.toString()); } else { System.err.println(pred + " says no to \"" + s + "\""); } } } 

jvm有一些function可以命名lambdas而不会失去幕后的优秀性能优化。

像这样的一些想法对我来说没问题:

 Predicate p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty()); 

这是一个可以想到的替代方案:

 static  Predicate nameIt(String name, Predicate pred) { return new Predicate() { public String toString() { return name; } public boolean test(T t) { return pred.test(t); } }; } 

这看起来很简单。 虽然我没有对它进行基准测试,但它似乎应该非常快。 它添加了一个对象和一个方法调用,它避免了装箱/拆箱开销。

缺点是您必须为要为其提供命名实例的每个function接口编写类似这样的函数。

这是我的解决方案(灵感来自andersschuller的解决方案,url为https://stackoverflow.com/a/23705160/1325574 )。 可能有一些极端情况(Classloading),这种实现不起作用,但对于最简单的情况,它可以工作。

我用我有限的jmh知识创建了一个小的性能测试: https ://gist.github.com/picpromusic/4b19c718bec5a652731a65c7720ac5f8

测量“命名” – 结果是为了实现@stuartmarks Naming(toString)Lambda-Expressions的调试目的的答案

 # 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 nameIt(String name, T obj) { if (Boolean.getBoolean("namedLambdasEnabled")) { Class clazz = (Class) 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; } } } 

你有其他解决方案吗? 也许某些没有性能影响的东西?