operators in predicate as argument in lambda expression

4.9k views Asked by At

I need to use Predicate as argument in lambda expression. I tried an example code but seeing compiler error. I see that the compiler is treating the same Predicate differently for different arguments. So Predicate arguments n -> true and n -> false works but n -> n%4 == 0 doesn't work.

The compiler error is:

The operator % is undefined for the argument type(s) Object, int

I fixed it (see the replacement code below) but I am asking should I have to fix it and why? I am not sure if I am missing something basic.

Here is the complete code:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class PredicateAsArgumentInLambdaExpression {

        public static int add(List<Integer> numList, Predicate predicate) {
            int sum = 0;
            for (int number : numList) {
                if (predicate.test(number)) {
                    sum += number;
                }
            }
            return sum;
        }

        public static void main(String args[]){
            List<Integer> numList = new ArrayList<Integer>();
            numList.add(new Integer(10));
            numList.add(new Integer(20));
            numList.add(new Integer(30));       
            numList.add(new Integer(40));       
            numList.add(new Integer(50));

            System.out.println("Add Everything: "+add(numList, n -> true));
            System.out.println("Add Nothing: "+add(numList, n -> false));
//          System.out.println("Add Less Than 25: "+add(numList, n -> n < 25)); Compiler says: The operator < is undefined for the argument type(s) Object, int
            System.out.println("Add Less Than 25: "+add(numList, n -> Integer.valueOf((int)n) < Integer.valueOf("25")));
//          System.out.println("Add 4 Multiples: "+add(numList, n -> n % 4 == 0)); //Compiler says: The operator % is undefined for the argument type(s) Object, int
            System.out.println("Add 4 Multiples: "+add(numList, n -> Integer.valueOf((int)n) % Integer.valueOf("4")==0));
        }
}

Commented out code are what's not working and the line immediately below each is the replacement code. The code works as is and as expected but I was expecting that the commented out code should have worked! What's is it that isn't ok with Predicate in java.util.function.Predicate here?. Please provide any link for specification page if you find the answer in.

2

There are 2 answers

3
Konstantin Yovkov On BEST ANSWER

What is happening is that you're using a raw java.util.function.Predicate, on which the test() method would look like:

public void test(Object o) { ... }

This is why you get a compile-time error: the argument type is Object and the numeric operators (<, >) are not applicable for the type Object.

However, if you use a generic java.util.function.Predicate with type-parameter of Integer, the test() method would look like:

public void test(Integer i) { ... }

In this case, the numeric operators (>, <) are valid for the provided argument type (Integer) and there's no need of casts.

Also, I've taken advantage of the Stream API in Java8 to shorten your method implementation:

public static int add(List<Integer> numList, Predicate<Integer> predicate) {
     return numList.stream().filter(predicate).mapToInt(i -> i).sum();
}

Having the method implemented like this, now all these statements will be perfectly valid:

System.out.println("Add Everything: "+add(numList, n -> true));
System.out.println("Add Nothing: "+add(numList, n -> false));
System.out.println("Add Less Than 25: "+add(numList, n -> n < 25));
System.out.println("Add 4 Multiples: "+add(numList, n -> n % 4 == 0));  
2
Michael Macha On

You're overlooking the generic parameter in your add method. If you switch from general Predicate to Predicate<Integer>, then n will automatically be cast to an Integer before operation.

In short, instead of:

public static int add(List<Integer> numList, Predicate predicate) {

you should try:

public static int add(List<Integer> numList, Predicate<Integer> predicate) {

Afterwards, your previous code of:

n -> Integer.valueOf((int)n) < Integer.valueOf("25")
n -> Integer.valueOf((int)n) % Integer.valueOf("4")==0

can simply be

n -> n < 25
n -> n % 4 == 0

which is additionally much more efficient.

It's just a question of matching what the method is expecting to what you are providing. As without the generic term, Java expects Predicate to receive type Object by default, and since Integer extends Object, it presumes that that is the type you are passing. With it, it expects Integer, and all of the methods and operators associated with Integer are exposed.

This is a somewhat invisible operation that occurs before your method is called, which is why your warning is on the lambda operation instead of in the method.

Good luck!