Printing debug info on errors with java 8 lambda expressions
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.
How to debug stream().map(...) with lambda expressions?
I usually have no problem debugging lambda expressions while using Eclipse or IntelliJ IDEA. Just set a breakpoint and be sure not to inspect the whole lambda expression (inspect only the lambda body).
Another approach is to use peek
to inspect the elements of the stream:
List<Integer> naturals = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13);
naturals.stream()
.map(n -> n * 2)
.peek(System.out::println)
.collect(Collectors.toList());
UPDATE:
I think you're getting confused because map
is an intermediate operation
- in other words: it is a lazy operation which will be executed only after a terminal operation
was executed. So when you call stream.map(n -> n * 2)
the lambda body isn't being executed at the moment. You need to set a breakpoint and inspect it after a terminal operation was called (collect
, in this case).
Check Stream Operations for further explanations.
UPDATE 2:
Quoting Holger's comment:
What makes it tricky here is that the call to map and the lambda
expression are in one line so a line breakpoint will stop on two
completely unrelated actions.Inserting a line break right after
map(
would allow you to set a break point for the lambda expression only.
And it’s not unusual that debuggers don’t show intermediate values of
areturn
statement. Changing the lambda ton -> { int result=n * 2; return result; }
would allow you to inspect result. Again, insert line
breaks appropriately when stepping line by line…
Is it possible (how) to get the name of a method reference at Runtime Java?
As you're saying that you only need this for debugging purposes, here is a trick (i.e. a dirty hack) that will allow you to do what you want.
First of all, your functional interface must be Serializable
:
@FunctionalInterface
public interface FooInterface extends Serializable {
void method();
}
Now, you can use this undocumented, internal-implementation-dependent and extremely risky code to print some information about the method reference targeted to your FooInterface
functional interface:
@FunctionalInterface
public interface FooInterface extends Serializable {
void method();
default String getName() {
try {
Method writeReplace = this.getClass().getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
SerializedLambda sl = (SerializedLambda) writeReplace.invoke(this);
return sl.getImplClass() + "::" + sl.getImplMethodName();
} catch (Exception e) {
return null;
}
}
}
When you call this method:
doStuff(Foo::aMethodReference);
You'll see the following output:
package/to/the/class/Foo::aMethodReference
Note 1: I've seen this approach in this article by Peter Lawrey.
Note 2: I've tested this with openjdk version "11" 2018-09-25
and also with java version "1.8.0_192"
.
Creating String representation of lambda expression
The simplest thing I could come up with is creating a "named predicate" that gives your predicates a name or description, basically anything that will be useful as a toString
:
public class NamedPredicate<T> implements Predicate<T> {
private final String name;
private final Predicate<T> predicate;
public NamedPredicate(String name, Predicate<T> predicate) {
this.name = name;
this.predicate = predicate;
}
@Override
public boolean test(T t) {
return predicate.test(t);
}
@Override
public String toString() {
return name;
}
public static void main(String... args) {
Predicate<Integer> isEven = new NamedPredicate<>("isEven", i -> i % 2 == 0);
System.out.println(isEven); // prints isEven
}
}
Arguably giving your predicates names or descriptions like this makes the code where you use them a bit easier to understand also:
Stream.of(1, 2, 3, 4)
.filter(isEven)
.forEach(System.out::println);
A stranger idea might be to derive a "structural" description of the predicate, i.e. what is the output for some given inputs? Obviously this would work best when the input set is finite and small (e.g. for enums, booleans or some other restricted set), but I guess you could try a small set of "random" integers for integer predicates as well:
private static Map<Boolean, List<Integer>> testPredicate(Predicate<Integer> predicate) {
return Stream.of(-35, -3, 2, 5, 17, 29, 30, 460)
.collect(Collectors.partitioningBy(predicate));
}
For isEven
, this would return something like {false=[-35, -3, 5, 17, 29], true=[2, 30, 460]}
, which I don't think is necessarily clearer than if you manually give them a description, but is perhaps useful for predicates that are not under your control.
How to get the MethodInfo of a Java 8 method reference?
No, there is no reliable, supported way to do this. You assign a method reference to an instance of a functional interface, but that instance is cooked up by LambdaMetaFactory
, and there is no way to drill into it to find the method you originally bound to.
Lambdas and method references in Java work quite differently than delegates in C#. For some interesting background, read up on invokedynamic
.
Other answers and comments here show that it may currently be possible to retrieve the bound method with some additional work, but make sure you understand the caveats.
How to print a FunctionA,B?
seems to be XY problem: you actually need to log errors in convenient way, but do not know how to refer class fields in code... There are two options:
- lombok: @FieldNameConstants - there are some issues
- implement annotation processor which will generate metamodel for your classes (or if you are on HBN you may use existing one: https://vladmihalcea.com/jpa-criteria-metamodel/)
Related Topics
Access to Private Inherited Fields via Reflection in Java
Technique or Utility to Minimize Java "Warm-Up" Time
Using Powermockito.Whennew() Is Not Getting Mocked and Original Method Is Called
Java Serialization with Non Serializable Parts
How to Copy a Java.Util.List into Another Java.Util.List
Read File from Resources Folder in Spring Boot
Hibernate Criteria: Joining Table Without a Mapped Association
What Is the Main-Stream Java Alternative to ASP.NET/Php
How to Write a Basic Swap Function in Java
Using Jaxb to Unmarshal/Marshal a List<String>
Consistency of Hashcode() on a Java String
How to Resolve Unable to Load Authentication Plugin 'Caching_Sha2_Password' Issue
Java 8 Streams Flatmap Method Example
Can One Do a for Each Loop in Java in Reverse Order