Creating String representation of lambda expression [duplicate]
Asked Answered
G

1

13

For debugging purposes I am trying to create string representations of lambda expressions (specifically of Predicates, though it would be interesting for other lambda expressions too) in Java 8. My idea would be something like this:

public class Whatever {

    private static <T> String predicateToString(Predicate<T> predicate) {
        String representation = ... // do magic
        return representation;
    }

    public static void main(String[] args) {
        System.out.println(Whatever.<Integer>predicateToString(i -> i % 2 == 0));
    }

}

And the output would be i -> i % 2 == 0 (or something logically equivalent). The toString() method seems to be of no help, the output is just something like com.something.Whatever$$Lambda$1/1919892312@5e91993f (which I guess is to be expected as toString() is not overridden).

I'm not sure whether something like this is even possible, e.g. with reflection, I certainly haven't been able to find anything on it so far. Any ideas?

Grad answered 16/5, 2014 at 20:51 Comment(10)
Debugging purposes? Can't you just look at the source?Mckay
That would require a decompiler.Metabolize
@SotiriosDelimanolis Of course I can always look at the source. But it would be nice to also have a printable representation.Grad
Can't you just copy the source then?Hotfoot
@JeroenVannevel You mean just writing a String representation manually every time? Possible, sure. But not very elegant.Grad
You can have macros in Java (#define). It's not nice but you can define a Predicate that at the same time also creates a String of the very same content. I wouldn't do it though.Elude
@Trilarion I'll look into that but it sounds pretty ugly. It's news to me though, that Java supports something similar to C precompiler macros.Grad
@Grad Technically macros aren't supported by Java itself, but you can always run your java source through a C preprocessor to get the same effectTakeover
@user3580294 True. But definitely not what I would want to do or force anyone else who uses my code to do. ;-)Grad
possible duplicate of Is it possible to retrieve lambda expression at runtime, and related to Printing debug info on errors with java 8 lambda expressionsSibilant
S
24

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.

Sinuous answered 16/5, 2014 at 21:56 Comment(4)
While this should work, the beauty of the lambda expression syntax is partially lost here as the wrapper object NamedPredicate has to be created with the new operator.Grad
@Grad True. I guess you could hide some of the ugliness like new and <> by making a static factory method instead (see e.g. all the Guava collection factory methods). I also added another idea for determining a description of sorts from any predicate after the fact - I'm not sure it's useful, but could be interesting to explore nonetheless!Sinuous
A factory method would improve the situation, sure. Still not ideal but in lack of a programatic way of extracting the expression this is probably the most elegant solution.Grad
I tried to generalize the solution. Look at https://mcmap.net/q/904836/-naming-tostring-lambda-expressions-for-debugging-purpose for a generic implementation.Furey

© 2022 - 2024 — McMap. All rights reserved.