Naming(toString) Lambda-Expressions for Debugging purpose

前端 未结 2 721
情歌与酒
情歌与酒 2020-12-22 06:09

Sometimes it is usefull to name lambdas. Especially when you pass them around as parameter.

A realy simple example is

public class Main {
    publi         


        
2条回答
  •  猫巷女王i
    2020-12-22 06:14

    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.

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

    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
    

    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.

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

    Do you have other solutions? Maybe something that does not have performance implications?

提交回复
热议问题