I would love to understand why the code below will not compile, and what the correct implementation for the desired functionality should be.
First the simple case, which is not my desired functionality but still strange to me.
My compile errors are:
GenericsTest.java:38: error: method usePredicate2 in class GenericsTest<X> cannot be applied to given types;
genericsTestChild.usePredicate2(testCollection); // Does not compile
^
required: Predicate<Collection<?>>
found: Predicate<Collection<Parent>>
reason: argument mismatch; Predicate<Collection<Parent>> cannot be converted to Predicate<Collection<?>>
where X is a type-variable:
X extends Parent declared in class GenericsTest
For some reason the wildcard does not match. A Collection<Parent>
is not a match to Collection<?>
when it is inside a Predicate
.
Then for my actual desired functionality:
GenericsTest.java:33: error: method usePredicate1 in class GenericsTest<X> cannot be applied to given types;
genericsTestParent.usePredicate1(testCollection); // Does not compile
^
required: Predicate<? super List<? super Parent>>
found: Predicate<Collection<Parent>>
reason: argument mismatch; Predicate<Collection<Parent>> cannot be converted to Predicate<? super List<? super Parent>>
where X is a type-variable:
X extends Parent declared in class GenericsTest
Collection
should be within bounds for ? super List
and Parent
is within bounds for ? super Parent
, so the combination should be within bounds, but apparently it is not.
Compilation was done with "javac" from Java 16, using the "-Xdiags:verbose" flags for additional information.
And the code to replicate this:
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
public class GenericsTest<X extends GenericsTest.Parent> {
/** A child class. */
public static class Child extends Parent { }
/** A parent class. */
public static class Parent { }
/** This predicate should be able to handle anything that is a sub-class of Collection containing anything that is a sub-class of Parent. */
public static class TestCollection implements Predicate<Collection<Parent>> {
@Override
public boolean test(Collection<Parent> parent) {
return false;
}
}
/** Desired functionality : predicate will consume something less specific than List<> of less specific than X. */
public void usePredicate1(Predicate<? super List<? super X>> predicate) { }
/** Another fail-case: Predicate will consume something that is exactly a Collection<> of anything. */
public void usePredicate2(Predicate<Collection<?>> predicate) { }
public static void tryIt() {
Predicate<Collection<Parent>> testCollection = new TestCollection();
GenericsTest<Parent> genericsTestParent = new GenericsTest<>();
genericsTestParent.usePredicate1(testCollection); // Does not compile
genericsTestParent.usePredicate2(testCollection); // Does not compile
GenericsTest<Child> genericsTestChild = new GenericsTest<>();
genericsTestChild.usePredicate1(testCollection); // Does not compile
genericsTestChild.usePredicate2(testCollection); // Does not compile
}
}