Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/343.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用java 8 lambda表达式打印错误的调试信息_Java_Debugging_Reflection_Lambda_Java 8 - Fatal编程技术网

使用java 8 lambda表达式打印错误的调试信息

使用java 8 lambda表达式打印错误的调试信息,java,debugging,reflection,lambda,java-8,Java,Debugging,Reflection,Lambda,Java 8,我想使用静态方法作为setter助手,捕捉异常并打印有关失败操作的调试信息。我不想只知道例外情况的细节。我想显示设置了什么属性,以便详细信息有助于快速调试问题。我正在使用Java8 我应该如何提供或检测正在设置的属性? 我希望删除示例中的“name”字符串并得到相同的结果。 我知道我不能对提供的setter方法使用反射,该方法被转换为lambda表达式,然后转换为BiConsumer 我得到了这个,但是需要提供属性名 /** setter helper method **/ private st

我想使用静态方法作为setter助手,捕捉异常并打印有关失败操作的调试信息。我不想只知道例外情况的细节。我想显示设置了什么属性,以便详细信息有助于快速调试问题。我正在使用Java8

我应该如何提供或检测正在设置的属性?

我希望删除示例中的“name”字符串并得到相同的结果。

我知道我不能对提供的setter方法使用反射,该方法被转换为lambda表达式,然后转换为BiConsumer

我得到了这个,但是需要提供属性名

/** setter helper method **/
private static <E, V> void set(E o, BiConsumer<E, V> setter,
        Supplier<V> valueSupplier, String propertyName) {
    try {
        setter.accept(o, valueSupplier.get());
    } catch (RuntimeException e) {
        throw new RuntimeException("Failed to set the value of " + propertyName, e);
    }
}
/**setter-helper方法**/
专用静态无效集(EO、双消费者设置器、,
供应商值供应商,字符串属性名称){
试一试{
accept(o,valueSupplier.get());
}捕获(运行时异常e){
抛出新的RuntimeException(“设置“+propertyName,e”的值失败);
}
}
例如:

    Person p = new Person();
    Supplier<String> nameSupplier1 = () ->  "MyName";
    Supplier<String> nameSupplier2 = () -> { throw new RuntimeException(); };
    set(p, Person::setName, nameSupplier1, "name");
    System.out.println(p.getName()); // prints MyName
    set(p, Person::setName, nameSupplier2, "name"); // throws exception with message  Failed to set the value of name
    System.out.println(p.getName()); // Does not execute
Person p=新人();
供应商名称供应商1=()->“我的名称”;
供应商名称供应商2=()->{抛出新的RuntimeException();};
set(p,Person::setName,名称供应商1,“名称”);
System.out.println(p.getName());//打印我的名字
set(p,Person::setName,名称供应商2,“名称”);//引发异常,消息无法设置名称的值
System.out.println(p.getName());//不执行

编辑:我知道反射对lambdas没有帮助。我知道AOP,我也知道这可以通过纯反射实现,但我想知道是否有更好的方法可以通过Java8实现,而Java7则不存在这种方法。在我看来应该是这样的。现在可以将setter方法传递给另一个方法。

如果希望方法引用作为唯一的输入,可以使用以下技巧将它们调试为可打印的名称:

public static void main(String[] args) {
  Person p = new Person();
  Supplier<String> nameSupplier1 = () -> "MyName";
  Supplier<String> nameSupplier2 = () -> { throw new RuntimeException(); };
  set(p, Person::setName, nameSupplier1);
  System.out.println(p.getName()); // prints MyName
  set(p, Person::setName, nameSupplier2); // throws exception with message
  System.out.println(p.getName()); // Does not execute
}

interface DebuggableBiConsumer<A, B> extends BiConsumer<A, B>, Serializable {}

private static <E, V> void set(
    E o, DebuggableBiConsumer<E, V> setter, Supplier<V> valueSupplier) {
  try {
    setter.accept(o, valueSupplier.get());
  } catch (RuntimeException e) {
    throw new RuntimeException("Failed to set the value of "+name(setter), e);
  }
}

private static String name(DebuggableBiConsumer<?, ?> setter) {
  for (Class<?> cl = setter.getClass(); cl != null; cl = cl.getSuperclass()) {
    try {
      Method m = cl.getDeclaredMethod("writeReplace");
      m.setAccessible(true);
      Object replacement = m.invoke(setter);
      if(!(replacement instanceof SerializedLambda))
        break;// custom interface implementation
      SerializedLambda l = (SerializedLambda) replacement;
      return l.getImplClass() + "::" + l.getImplMethodName();
    }
    catch (NoSuchMethodException e) {}
    catch (IllegalAccessException | InvocationTargetException e) {
      break;
    }
  }
  return "unknown property";
}
publicstaticvoidmain(字符串[]args){
人员p=新人员();
供应商名称供应商1=()->“我的名称”;
供应商名称供应商2=()->{抛出新的RuntimeException();};
set(p,Person::setName,nameSupplier1);
System.out.println(p.getName());//打印MyName
set(p,Person::setName,nameSupplier2);//引发异常并显示消息
System.out.println(p.getName());//不执行
}
接口DebuggableBiConsumer扩展双消费者,可序列化{}
私有静态空集(
E o,调试人员(消费者设定者,供应商价值供应商){
试一试{
accept(o,valueSupplier.get());
}捕获(运行时异常e){
抛出新的RuntimeException(“未能设置“+name(setter),e)的值);
}
}
私有静态字符串名称(DebuggableBiConsumer setter){
for(类cl=setter.getClass();cl!=null;cl=cl.getSuperclass()){
试一试{
方法m=cl.getDeclaredMethod(“WriterReplace”);
m、 setAccessible(true);
对象替换=m.invoke(setter);
如果(!(序列化DLAMBDA的替换实例))
break;//自定义接口实现
序列化DLAMBDA l=(序列化DLAMBDA)替换;
返回l.getImplClass()+“:”+l.getImplMethodName();
}
catch(NoSuchMethodException){}
捕获(IllegalAccessException | InvocationTargetException e){
打破
}
}
返回“未知财产”;
}
其局限性在于,它将为lambda表达式打印不太有用的方法引用(对包含lambda代码的合成方法的引用),并为接口的自定义实现打印
“未知属性”

如何

源代码:

package test;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;

public class Person {
    public String getName() {
        return "Michael";
    }

    public static void main(String[] args) throws
            NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Serializable s = (Function<Person, String> & Serializable) Person::getName;

        Method method = s.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(true);
        SerializedLambda serializedLambda = (SerializedLambda) method.invoke(s);

        System.out.println(serializedLambda.getImplClass().replace("/", ".")
                + "::" + serializedLambda.getImplMethodName());
    }
}

实际上,您需要查找方法“writeReplace”,调用它并计算其返回值,返回值的类型为SerializedLambda。

我不喜欢问题标题。建议或编辑是受欢迎的。我不确定这是否有帮助,但可能对调试信息有帮助:谢谢@Zavior,但我想要一些不那么侵入性的东西,不是拦截每个调用,而是捕获错误并仅在出现错误时工作。这是一个奇怪的问题。当失败的不是属性而是供应商时,打印属性名称如何帮助解决问题?如果属性失败,则其setter方法将包含在堆栈跟踪中。如果不是,那么就把注意力放在失败的东西上,而不是放在未涉及的属性上。@Holger当失败的不是代码而是输入数据时,它会给你一个非常有用的上下文。如果数据错误,则处理可能会失败,并且错误的数据不足以进行调试,您需要知道该数据是什么,例如,它是人名还是电话号码。供应商可能是一个简单的lambda,如
s->s。子字符串(1)
。好的
序列化lambda
是保持不同实现之间兼容性的形式。理论上,只有
writeReplace
方法可以用不同的机制替代。如果你想完全安全,你必须将对象写为实对象,例如,写入一个字节数组并解析输出。输出必须包含符合一致性实施的
序列化DLAMBDA
。是的,它只在例外情况下运行缓慢。您使用了哪种编译器?我发现当前的eclipse测试版没有正确编译它。我使用了netbeans和jdk-8-fcs-bin-b128。但我现在用几个版本测试了它。使用
netbeans
和/或
javac
。对于eclipse,我们必须等待更新的版本……不,问题是eclipse编译器没有在应该生成的位置生成可序列化的lambda。
writeObject
方法只会抛出一个
NotSerializableException
;让我们看看修复需要多长时间。对于lambda表达式(
(?
test.Person::getName