Search code examples
javalambdatostringjava-8predicates

Creating String representation of lambda expression


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?


Solution

  • 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.