Naming (toString) Lambda expressions for debugging purpose

It is sometimes useful to call lambda. Especially when you pass them as a parameter.

A fairly simple example:

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 + "\""); } } } 

It would be nice to have some jvm functionality to denote lambdas without losing a lot of performance optimizations.

Something like this would be good for me:

 Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty()); 
0
source share
2 answers

Here is an alternative that comes to mind:

 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); } }; } 

It seems pretty simple. Although I did not compare it, it seems that it should be pretty fast. It adds one object and one method call, and it avoids the overhead of boxing / unboxing.

The disadvantage is that you need to write a small function like this for each functional interface for which you want to provide named instances.

+1
source

This is my solution (inspired by the andersschuller solution at fooobar.com/questions/969406 / ... ) for the problem. There may be some Classloading cases where this implementation does not work, but for the simplest cases it works.

I created a small performance test with my limited knowledge of jmh: https://gist.github.com/picpromusic/4b19c718bec5a652731a65c7720ac5f8

"Named" results are measured to implement @stuartmarks answer. Naming (toString) lambda expressions for debugging purposes

 # 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 

As you can see, this is about 2 times slower than using an unnamed lambda. Therefore, be careful when setting -DnamedLambdasEnabled = true. The premonition for me is that calling β€œString” on Real Lambda is surprisingly expensive. Maybe someone can explain this, or my jmh test is stupid.

Here is the code:

 /** * 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 affect performance?

0
source

Source: https://habr.com/ru/post/969407/


All Articles