1

This looks like very basic but I fail to understand why my code doesn't compile.

I have a class Person with a String name attribute and a getName() getter.

I create an Employee class extends Person

I have this method :

public Predicate<? extends Person> startsA() {
        return p -> p.getName().startsWith("A");
}

So I want this method to work with all subclasses of Person

But this code doesn't compile :

startsA().test(new Employee());
startsA().test(new Person());

I don't understand why

5
  • Just change Predicate<? extends Person> to Predicate<Person>. Commented May 12, 2023 at 15:52
  • 2
    Or if you want to allow the method to return Predicate<Employee>, use public <P extends Person> Predicate<P> Commented May 12, 2023 at 15:55
  • 1
    Does this answer your question? Give examples of functions which demonstrate covariance and contravariance in the cases of both overloading and overriding in Java? Commented May 12, 2023 at 15:57
  • ok thanks. Isn't <? extends Person> and <P extends Person> <P> the same thing ? Commented May 12, 2023 at 15:58
  • 2
    No, they are not the same thing. Every Predicate has a specific, non-wildcard type. Predicate<? extends Person> means “this Predicate’s specific type is either Person or some class that inherits Person, but I don’t know what that specific type is.” When you try to call test(new Person()), it is not type-safe, because the specific type of the Predicate might be Employee, or Customer, or OlympicAthlete. ? extends Person means the compiler doesn’t know which specific type the Predicate requires. Commented May 12, 2023 at 16:14

2 Answers 2

3

The reason your code doesn't compile is because you have defined the startsA() method to return a Predicate of a wildcard type that extends Person. The wildcard type represents an unknown subtype of Person,

Change startsA() method to return a Predicate<Person> instead of using a wildcard type:

public Predicate<Person> startsA() {
    return p -> p.getName().startsWith("A");
}
Sign up to request clarification or add additional context in comments.

1 Comment

Just to add to this answer: any Employee is a Person, but any Employee cannot be a subtype of an unknown subclass of Person (which ? extends Person implies). Same holds for any subclass of Person (ie Employee is just an example here).
1

Generics are one meta level removed. You need to walk a mile in the shoes of the compiler to understand them.

The mistake I often see with generics, and the one you seem to make, is that you think Predicate<? extends Person> means 'this is a predicate that can test any person. An Employee, a Customer - doesn't matter, hence, 'whatever extends Person' right.

But that is not what that means.

After all, imagine it meant that. What is Predicate<Person> then? A predicate that can test only specifically new Person() but not new Employer()? Java doesn't work that way - any instance of Employer can be used as a Person so that would be utterly bizarre.

In fact, Predicate<Person> means what you want here: A predicate object that can test any person.

So what does Predicate<? extends Person> mean? This:

A predicate that can test... I don't actually know what it can test. All I know is, whatever it is, it's some type that is either Person or some subtype thereof.

So what point is there to such a thing? Not all that much in this specific case (for predicate). Let's instead think of lists. We have 3 different types that are relevant (remember, Integer and Double both extend Number, and Number extends Object)

  • List<? extends Number>
  • List<? super Number>
  • List<Number>

You can pass a List<Integer> object to a method that takes a List<? extends Number>. You cannot pass it to a method that takes a List<Number>. Why not - Integer extends Number, right? Ah, but inside generics, that's invariant, as in, no, you can't just replace a type with its subtype. After all, I can add a Double object to a List<Number>. I can add Number objects to my list, a Double object is a Number object, QED.

But, adding a Double object to a List<Integer>, that's no good. Hence, a List<? extends Number> is required - try it, you cannot add things to such a list. For the same reason: List<? extends Number> does not mean: "A list that can hold numbers - any numbers". Quite the opposite. It means: "A list that can hold.. I do not know what it can hold (hence the question mark!) - all I know is, whatever it holds, it's Number. Or any subclass of Number). Given that, list.get(5) WILL return an expression of type Number (given the few things we know, that is safe), but you can't add anything to it (except for the academic case of a literal null, which is all types).

For Predicate in particular, Predicate<? extends> is completely useless, that shouldn't ever show up in any case. Predicate<? super X> can be quite convenient. After all, that means: "A predicate that can test.. well I do not know what it can test. What I do know, is that it is either X, or some super type of X". Given that information, .test(instanceOfX) is fine.

2 Comments

Thank you very much. You perfectly understood what I failed to understand when it comes to generics
It's useful to mention the PECS (Producer extends, Consumer super) rule. Since Predicate is solely a consumer of its type parameter, it can always be used with super wildcards.

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.