Printing debug info on errors with java 8 lambda expressions
What about that?
Source code:
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());
}
}
Output:
test.Person::getName
You actually need to look up the method "writeReplace", call it and evaluate its return value which is of type SerializedLambda.
In case you expect method references as the only input, you can debug them to printable names with the following trick:
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";
}
The limitations are that it will print not very useful method references for lambda expressions (references to the synthetic method containing the lambda code) and "unknown property"
for custom implementations of the interface.