Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Predicates in Java 8

I have a program that analyzes text for certain attributes. The basic structure of the code is the same for all attributes it only varies in one line: webDataField.containsAttributeXYZ(); as seen here:

for (int index = 0; index < fields.size(); index++) {
    WebDataField webDataField = fields.get(index);
    boolean expectedCondition = webDataField.containsAttributeXYZ(); // Varies at this line..

    Double score = evaluateBooleanCondition(actualCondition, expectedCondition);

    WebDataFields fieldName = webDataField.getFieldName();
    ...
}

Instead of writing the same code for each condition (& repeating myself in code) I'd like to write the main body of the code once and pass the condition (which evaluates to true or false) to the method.

I am new to Java Predicate but if I understand correctly this is exactly the function of predicates in Java.

Does this force me to write each condition in its own class and then have that class implement the Predicate interface?

If so, the function (which is previously declared in a class that contains similar functions) will need to be converted to something like:

class ContainsAttributeXYZ implements Predicate<Boolean>

This would cause the code to become disoriented and trigger a large increase in the number of classes in the program (as every function needs to be converted into a class)

Or did I misunderstand something about how Predicates work in Java? Is there another way of doing this?

Thanks

like image 568
Little Kevin Avatar asked May 10 '17 19:05

Little Kevin


2 Answers

Here is an example method that takes a Predicate as an argument:

void processFields(List<WebDataField> fields, Predicate<WebDataField> predicate) {
    for (WebDataField webDataField : fields) {
        boolean expectedCondition = predicate.test(webDataField);
    }
}

and you would call it by passing a method reference, like this:

processFields(fields, WebDataField::containsAttributeXYZ);
processFields(fields, WebDataField::containsAttributeABC);

Where containsAttributeXYZ and containsAttributeABC are methods of WebDataField class that return boolean. Or you can create a lambda inline without modifying WebDataField. E.g. :

processFields(fields, w -> w.getFieldName().length() > 5);

You don't need to create a class that implements Predicate

like image 195
Manos Nikolaidis Avatar answered Oct 14 '22 04:10

Manos Nikolaidis


You don't need to create a new class to create a Predicate. This is because Java 8 added a lambda syntax, which you can think of as a shorthand for anonymous inner classes implementing an interface with only one method. Here is an example:

Predicate<Boolean> containsXYZ = b -> {
    return !b;
};

This is equivalent to:

Predicate<Boolean> containsXYZ = new Predicate<Boolean>() {
    public boolean test(Boolean b) {
        return !b;
    }
};

If your predicate just returns the result of a single expression, it can be shortened to:

Predicate<Boolean> containsXYZ = b -> !b;

Alternatively, a function can be a reference to a method:

Predicate<Boolean> containsXYZ = MyClass::myMethod; // for static methods, or
Predicate<Boolean> containsXYZ = this::myMethod; // for instance methods

This is a one-line predicate that returns true if the input is false, and vice versa. Now, for your use case, you might want something like this:

static void checkAttributes(Predicate<WebDataField> containsAttributeXYZ) {
    for (WebDataField webDataField : fields) {
        boolean expectedCondition = containsAttributeXYZ.test(webDataField);
        Double score = evaluateBooleanCondition(actualCondition, expectedCondition);
        WebDataFields fieldName = webDataField.getFieldName();

        ...
    }
}

For a thorough explanation of how lambdas work, read the Java Tutorial.

like image 45
Brian McCutchon Avatar answered Oct 14 '22 04:10

Brian McCutchon