0

I have to perform and() on my business objects that implement Predicate<MoneyOperation>.

Code where problem occurs is the line with and() call:

        Predicate<? super MoneyOperation> predicate =
           new MoneyOperationDateWithRadioButtonsPredicate(newv, 
                  thisDayRdBtn.getValue(), date);

        filteredData.setPredicate(filteredData.getPredicate().and(predicate));

Error:

The method and(Predicate<? super capture#14-of ? super MoneyOperation>) 
in the type Predicate<capture#14-of ? super MoneyOperation> is not applicable 
for the arguments (Predicate<capture#15-of ? super MoneyOperation>)

Although when I pass predicate to setPredicate() without calling and() compiler does not raise any errors.

MoneyOperationDateWithRadioButtonsPredicate class:

public class MoneyOperationDateWithRadioButtonsPredicate extends MoneyOperationFieldPredicate<LocalDate> { ... }

MoneyOperationFieldPredicate class:

public abstract class MoneyOperationFieldPredicate<T> implements Predicate<MoneyOperation> { ... }
janlan
  • 477
  • 1
  • 5
  • 19
  • Problem is that your first `Predicate super MoneyOperation>` uses a wildcard, i.e. an unnamed generic parameter. Taking at the look at [`Predicate#and(...)`](https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html), the `Predicate` passed in as an argument must have generic type `? super T`, whereby `T` is the generic parameter of the first `Predicate` (which is an anonymous parameter) so... this won't work (since both parameters do not have any connection except `super MoneyOperation`). I would recommend to drop the `? super`. – Turing85 Aug 25 '19 at 20:53

2 Answers2

4

The problem here are the captures. Let's just see what those constrains mean:

Predicate<? super MoneyOperation> is a predicate that can take a MoneyOperation or maybe it's super class and returns a boolean. This means it can be a Predicate<Object> that takes an Object and returns a boolean.

Now there is a potential problem:
Let's assume that MoneyOperation extends GenericOperation and implements SomeInterface.
The first Predicate<? super MoneyOperation> might in fact be a Predicate<GenericOperation> and the second Predicate<? super MoneyOperation> a Predicate<SomeInterface>. (The Java compiler can't tell) Passing a MoneyOperation to both of them is fine, but combining them would require restricting it's type to Predicate<MoneyOperation>. While this would be fine for Predicate, it might not be for other cases.

And that's why Java doesn't allow this.
Use Predicate<MoneyOperation> if you know it is one.

Johannes Kuhn
  • 14,778
  • 4
  • 49
  • 73
  • I would add that you can convert a `Predicate super T>` to a `Predicate` by assigning a method reference to a variable: `Predicate p = lowerBoundedPredicate::test;`. – Andy Turner Aug 25 '19 at 21:10
3

It's just a limitation of the type system, because Java doesn't use the fact that Predicate has no producer methods (in the sense of PECS) to handle the type differently.

Consider two List<? super Integer> instances: you can't do list1.addAll(list2), for the same reason that you can't do predicate1.and(predicate2) here: that is guarding against the fact that list1 might be a List<Integer>, whereas list2 is a List<Object>: you shouldn't add Objects to a list that you expect only to contain Integers.

That is an actual problem for Lists, because List has producer methods (e.g. List.get(int)), i.e. methods that are meant to give you instances of a particular type. Predicate only has consumer methods (one, to be exact, Predicate.test<T>), so there is no situation in which a Predicate would give you an object of an unexpected type.

However, the compiler does not consider whether a class has no producer methods (or no consumer methods): it assumes both are present (indeed, they could be present on subclasses), and so it prevents you from doing this. Annoying, maybe, but that's how it is.

The easiest way is to construct a lambda to explicitly test the two predicates:

setPredicate(a -> predicate1.test(a) && predicate2.test(b))

You may be able to use this one weird trick:

setPredicate(Stream.of(predicate1, predicate2).map(a -> a).reduce(Predicate::and))

But I have never quite been satisfied whether that works by specification, or by quirk of implementation.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243