2

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.

17
  • 5
    What about using Predicate<Integer>? Commented Jun 14, 2015 at 15:53
  • 2
    @user3320018 What do you mean? n -> true and n -> false works fine for me with Predicate<Integer>. Commented Jun 14, 2015 at 15:56
  • 1
    @user3320018: Just look at kocko's answer, he tells you how to use the Predicate<Integer>. Commented Jun 14, 2015 at 15:59
  • 1
    @user3320018 "no compiler error in case of n -> true and n -> false" do you want to get compiler error for these cases? If yes, why? If not, then what is the problem (it works fine)? Commented Jun 14, 2015 at 15:59
  • 2
    "why is there a down vote for my question" I am guessing that someone didn't like the fact that your problem is caused by raw types, which are explained in every tutorial about generics. Commented Jun 14, 2015 at 16:17

2 Answers 2

5

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));  
Sign up to request clarification or add additional context in comments.

3 Comments

I replaced my method with your's but seeing compiler error: The method sum() is undefined for the type Stream<Integer>
As an alternative IntPredicate can be used which works for primitive int type.
@user3320018, you don't have any stream API in provided code snippet, so it's unlikely that we can help you with this compiler error with this question.
1

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!

2 Comments

would it work if I used the < operator and gave a double to compare against. Or any other type for that matter. Predicate<Integer> seems limiting doesn't it?
It will absolutely work. Look up "autoboxing". Double and Integer both extend Number, so you're covered; additionally, you can't define a generic to be a primitive type like int, as those are entirely their own species of class in Java. So you won't be limited at all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.