Java 为调试目的命名(toString)Lambda表达式
有时将lambdas命名是有用的。尤其是当您将它们作为参数传递时 一个非常简单的例子是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(
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