12

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?

Sandeep Chatterjee
  • 3,220
  • 9
  • 31
  • 47
blalasaadri
  • 5,990
  • 5
  • 38
  • 58
  • Debugging purposes? Can't you just look at the source? – Sotirios Delimanolis May 16 '14 at 20:52
  • 2
    That would require a decompiler. – SLaks May 16 '14 at 20:53
  • @SotiriosDelimanolis Of course I can always look at the source. But it would be nice to also have a printable representation. – blalasaadri May 16 '14 at 20:53
  • Can't you just copy the source then? – Jeroen Vannevel May 16 '14 at 20:54
  • @JeroenVannevel You mean just writing a String representation manually every time? Possible, sure. But not very elegant. – blalasaadri May 16 '14 at 20:56
  • 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. – NoDataDumpNoContribution May 16 '14 at 20:56
  • @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. – blalasaadri May 16 '14 at 21:02
  • @blalasaadri Technically macros aren't supported by Java itself, but you can always run your java source through a C preprocessor to get the same effect – awksp May 16 '14 at 21:04
  • @user3580294 True. But definitely not what I would want to do or force anyone else who uses my code to do. ;-) – blalasaadri May 16 '14 at 21:05
  • possible duplicate of [Is it possible to retrieve lambda expression at runtime](http://stackoverflow.com/q/20629068/2711488), and related to [Printing debug info on errors with java 8 lambda expressions](http://stackoverflow.com/q/21860875/2711488) – Holger May 18 '14 at 19:15

1 Answers1

23

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.

andersschuller
  • 13,509
  • 2
  • 42
  • 33
  • 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. – blalasaadri May 16 '14 at 22:02
  • @blalasaadri 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! – andersschuller May 16 '14 at 22:16
  • 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. – blalasaadri May 17 '14 at 20:19
  • I tried to generalize the solution. Look at http://stackoverflow.com/a/42876841/1325574 for a generic implementation. – Sebastian Mar 18 '17 at 16:27