Java 为调试目的命名(toString)Lambda表达式

Java 为调试目的命名(toString)Lambda表达式,java,debugging,lambda,jvm,tostring,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(

有时将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 + "\"");
        }
    }
}
公共类主{
公共静态void main(字符串[]args){
谓词p=nameIt(“isNotEmpty”,(s)->!s.trim().isEmpty());
maybePrint(“你好”,p);
可打印(“,p);
}
静态void可以打印(ts,谓词pred){
if(试验前)){
System.out.println(s.toString());
}否则{
System.err.println(pred+”对\“+s+”\”说不);
}
}
}
如果jvm能够提供一些功能来命名lambdas,而不失去幕后的性能优化,那就太好了

有些人认为这样对我来说很好:

Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty());
谓词p=nameIt(“isNotEmpty”,(s)->!s.trim().isEmpty());
这是我针对这个问题的解决方案(灵感来源于Anderschuller at的解决方案)。在某些情况下(类加载),这种实现可能不起作用,但对于最简单的情况,它可以起作用

我用自己有限的jmh知识创建了一个小型性能测试:

“命名”结果是为@stuartmarks答案的实现而测量的

正如您所看到的,它大约比使用未命名的lambda慢2倍。因此,请小心设置-DnamedLambdasEnabled=true。对我来说,有趣的是,在真正的lambda上调用toString的费用出奇地高。也许有人能解释,或者我的jmh测试很愚蠢

代码如下:

/**
 * 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;
        }
    }
}
/**
*Helper类为lambda提供一个名称(“toString”),以便进行调试
*
*/
公共类LambdaNamer{
将私有静态方法转换为_字符串;
静止的{
试一试{
TO_STRING=Object.class.getMethod(“toString”);
}catch(NoSuchMethodException | SecurityException e){
抛出新的RuntimeException(“在丹麦州有一些坏东西!”);
}
}
/**
*覆盖给定lambda的toString“方法”。
* 
*@param name to lambda的字符串结果
*@param obj要封装的lambda
*@返回命名的lambda
*/
公共静态T nameIt(字符串名,T obj){
if(Boolean.getBoolean(“namedLambdasEnabled”)){
类clazz=(类)obj.getClass();
类[]接口=clazz.getInterfaces();
return(T)Proxy.newProxyInstance(//
obj.getClass().getClassLoader()//
接口//
(对象代理、方法、对象[]参数)->{
if(TO_STRING.equals(方法)){
返回名称;
}否则{
返回方法.invoke(obj,args);
}
});
}否则{
返回obj;
}
}
}
你有其他解决办法吗?可能是一些对性能没有影响的问题?

这是我针对该问题的解决方案(灵感来自Anderschuller at的解决方案)。在某些情况下(类加载),这种实现可能不起作用,但对于最简单的情况,它可以起作用

我用自己有限的jmh知识创建了一个小型性能测试:

“命名”结果是为@stuartmarks答案的实现而测量的

正如您所看到的,它大约比使用未命名的lambda慢2倍。因此,请小心设置-DnamedLambdasEnabled=true。对我来说,有趣的是,在真正的lambda上调用toString的费用出奇地高。也许有人能解释,或者我的jmh测试很愚蠢

代码如下:

/**
 * 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;
        }
    }
}
/**
*Helper类为lambda提供一个名称(“toString”),以便进行调试
*
*/
公共类LambdaNamer{
将私有静态方法转换为_字符串;
静止的{
试一试{
TO_STRING=Object.class.getMethod(“toString”);
}catch(NoSuchMethodException | SecurityException e){
抛出新的RuntimeException(“在丹麦州有一些坏东西!”);
}
}
/**
*覆盖给定lambda的toString“方法”。
* 
*@param name to lambda的字符串结果
*@param obj要封装的lambda
*@返回命名的lambda
*/
公共静态T nameIt(字符串名,T obj){
if(Boolean.getBoolean(“namedLambdasEnabled”)){
类clazz=(类)obj.getClass();
类[]接口=clazz.getInterfaces();
return(T)Proxy.newProxyInstance(//
obj.getClass().getClassLoader()//
接口//
(对象代理、方法、对象[]参数)->{
if(TO_STRING.equals(方法)){
返回名称;
}否则{
返回方法.invoke(obj,args);
}
});
}否则{
返回obj;
}
}
}

你有其他解决办法吗?可能是没有性能影响的东西?

我想到了一个替代方案:

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

static Predicate nameIt(字符串名,Predicate这里有一个备选方案:

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

static Predicate nameIt(String name,Predicate)方法引用类似于“named”lambda基于方法引用的lambda在运行时没有可调试字符串。无论是作为字符串结果还是在调试器(至少在eclipse中)视图中都没有。您是对的,但现在我了解到您有时可以获得它,请参阅[如何获取Java 8方法引用的MethodInfo?]()。我让它正常工作,对它感兴趣?我非常喜欢以下想法:。我认为它只适用于非静态方法引用,并且您需要能够使用运行时字节码生成库。方法引用类似于“命名”lambdaA l