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

时间:2017-03-18 16:22:32

标签: java debugging lambda jvm tostring

有时命名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());

2 个答案:

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

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