Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logging the conditional outcome in java8 Predicate in a concise manner

I have a code like this:

Predicate<String> notNull = input -> (input != null);
Predicate<String> notEmpty = input -> (input.trim().length() > 0);

Its being used by my validator method like this:

public boolean isInputValid(String input){
    return (notNull.and(notEmpty)).test(input);
}

Whenever i call isInputValid(<myInputString>) method, It will return me true or false, However, if i wanted to log which condition exactly failed, how could i do that in Java8 without converting my Predicates notNull and notEmpty into methods.

The example below which converts Predicate notNull into a method will achieve my goal of logging the problem with input, but its too verbose. Is there a better/more-concise way to do it?

private Predicate<String> notNull(){
    return input -> {
        if(input == null){
            log.warn("NULL input");
            return false;
        }
        return true;
    };
}
like image 598
JavaTec Avatar asked Sep 08 '26 05:09

JavaTec


2 Answers

One approach is to create a higher-order function that wraps some logging code around an existing predicate and returns an predicate that can be used in place of the original. Here's an example of such a function:

static <T> Predicate<T> loggingPredicate(String label, Predicate<? super T> pred) {
    return t -> {
        boolean r = pred.test(t);
        System.out.printf("%s: %s => %s%n", label, t, r);
        return r;
    };
}

This prints out the input and result for each call. It also takes a label so that you can tell from the log which predicate is being evaluated. You could adjust this so that it only prints a log message if the predicate returns false, or something like that. And of course you could adjust this to use whatever logging framework you like instead of printing to stdout.

Then, you can take your existing predicates and wrap them in a calls to this function:

Predicate<String> notNull = loggingPredicate("notNull", input -> (input != null));
Predicate<String> notEmpty = loggingPredicate("notEmpty", input -> (input.trim().length() > 0));

Then use notNull and notEmpty as before. Every time one of them is evaluated, you'll get a log message.

like image 59
Stuart Marks Avatar answered Sep 10 '26 19:09

Stuart Marks


There is no inbuilt solution to this, but you can create your custom loggable Predicate to do the job.

@FunctionalInterface
interface LPredicate<T> extends Predicate<T>{
    default boolean testAndLog(T t, String prefix){
        boolean result = this.test(t);
        if(!result){
            log.warn(prefix + " " + t);
        }
        return result;
    }
}

FULL TESTABLE CODE

import java.util.function.Predicate;

public class FunctionLog {
    static MLog log = new MLog();

    @FunctionalInterface
    interface LPredicate<T> extends Predicate<T>{
        default boolean testAndLog(T t, String prefix){
            boolean result = this.test(t);
            if(!result){
                log.warn(prefix + " " + t);
            }
            return result;
        }
    }

    static LPredicate<String> notNull = input -> (input != null);
    static LPredicate<String> notEmpty = input -> (input.trim().length() > 0);  

    public static void main(String[] args) {        
        System.out.println("Case1: " + isInputValid(""));
        System.out.println("Case2: " + isInputValid(null));
        System.out.println("Case3: " + isInputValid("hello"));
    }

    public static boolean isInputValid(String input){       
        return notNull.testAndLog(input, "NULL Check Failed") && notEmpty.testAndLog(input, "EMPTY Check Failed");
    }
}

/**
 * Just a mock log class
 *
 */
class MLog {
    void warn(String str) {
        System.out.println(str);
    }
}
like image 37
Amith Kumar Avatar answered Sep 10 '26 18:09

Amith Kumar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!