我想使用静态方法作为setter helper来捕获异常并打印有关失败操作的调试信息。我不希望只有例外细节。我想显示正在设置的属性,以便详细帮助快速调试问题。我正在使用Java 8。
我应该如何提供或检测正在设置的属性?
我希望删除示例中的“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);
}
}
示例:
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
编辑:我知道反射对lambda不起作用。我知道AOP,我知道这也可以用纯粹的反射来制作,但我想知道是否有更好的方法来完成Java 7中不存在的Java 7。这对我来说应该是。现在可以做将setter方法传递给另一个方法。
答案 0 :(得分:20)
如果您希望方法引用作为唯一输入,可以使用以下技巧将它们调试为可打印的名称:
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";
}
限制是它将为lambda表达式(对包含lambda代码的合成方法的引用)和"unknown property"
打印不是非常有用的方法引用,用于接口的自定义实现。
答案 1 :(得分:1)
那怎么办?
源代码:
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());
}
}
输出:
test.Person::getName
您实际上需要查找方法“ writeReplace”,对其进行调用并评估其返回值,该返回值的类型为SerializedLambda。