Search code examples
javalambdafunctional-programmingfunctional-interface

Getting some kind of representation of a Lambda Expression at runtime


I'd like to use something like this to check some conditions, but the problem is that I need to know which condition failed and log the exception, this way does achieve it but relies on knowing the position at which it was checked inside the code which isn't ideal, is there any other nicer way of doing this? (without making calling the function much longer)

matchOrThrow(
        () -> 1 == 2,
        () -> 1 == 1,
        () -> a > b,
        () -> c == null
);
public static void matchOrThrow(BooleanSupplier... conditions) {
    int i = 1;
    for (BooleanSupplier condition : conditions) {
        if (Boolean.FALSE.equals(condition.getAsBoolean())) {
            throw new CustomException("Condition check n_" + i + " failed");
        }
        i++;
    }
}

Solution

  • You might be interested at looking at the Decorator design patter.

    Namely, you can create a decorating implementation of the Functional interface of your choice. It seems like you Predicate is more suitable for than a BooleanSupplier, therefore the example below illustrates a throwing Predicate, which expects a predicate, producer of the target exception, message and logger as arguments and its implementation of test() delegates to the wrapped predicate to evaluate the condition.

    The instances of the trowing Predicate can be used anywhere, where Predicate is expected.

    public class ThrowingLoggPredicate<T> implements Predicate<T> {
        private Predicate<T> predicate;
        private Function<String, RuntimeException> exceptionFactory;
        private String messageShort;
        private String format;
        private Logger logger;
        
        public ThrowingLoggPredicate(Predicate<T> predicate,
                                     Function<String, RuntimeException> exceptionFactory,
                                     String messageShort, String format,
                                     Logger logger) {
            
            this.predicate = predicate;
            this.exceptionFactory = exceptionFactory;
            this.messageShort = messageShort;
            this.format = format;
            this.logger = logger;
        }
        
        public boolean test(T t) {
            if (!predicate.test(t)) {
                RuntimeException e = exceptionFactory.apply(messageShort);
                String messageVerbose = String.format(format, t);
                logger.log(Level.ERROR, messageVerbose, e);
                throw e;
            }
            return true;
        }
        
        public static <T> boolean allMatch(Collection<Predicate<T>> predicates, T t) {
            
            return predicates.stream().allMatch(p -> p.test(t));
        }
    }