17

Assume we have a predicate declared as Predicate<? super SomeClass>. I would naively expect it to be applicable to any superclass of SomeClass up the hierarchy, including Object.

However this predicate is not applicable to Object. I get the following error:

The method test(capture#3-of ? super SomeClass) in the type Predicate is not applicable for the arguments (Object)

Demo.

Why is Predicate<? super SomeClass> not applicable to an instance of Object?

The code:

import java.util.*;
import java.lang.*;
import java.io.*;
import java.net.URL;
import java.util.function.Predicate;


/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Predicate<? super URL> p = u -> u.getFile().isEmpty();
        p.test(new Object());
    }
}
Naman
  • 27,789
  • 26
  • 218
  • 353
lexicore
  • 42,748
  • 17
  • 132
  • 221
  • Do you really want to use super? Looking at the code in the demo it seems as if you want "? extends SomeClass"? – ewramner May 09 '18 at 08:36
  • 2
    @ewramner This is not practical/real code. I'm simply trying to understand how ` super T>` works. – lexicore May 09 '18 at 08:36
  • 1
    @ewramner *I think you should be able to use `Object`* - I have demonstrated that this is not the case. – lexicore May 09 '18 at 08:44
  • 3
    fyi take a look at: [PECS](https://stackoverflow.com/q/2723397) , it's a pretty good rule of thumb to keep. – Jorn Vernee May 09 '18 at 08:52
  • What you want in your code is a variable of type `Predicate`, a `Predicate super X>` behaves by design like `Predicate` but allows more flexibility when chosing a concrete implementation (There is no `= new Predicate super X>()`, you have to specify a concrete type). You can for example take an implementation that is `Predicate`, because that code also works fine on X. It's not about allowing any T in the `test` method. – zapl May 09 '18 at 08:55
  • The only difference between a variable (or function parameter) of type `Predicate super X>` and one of type `Predicate` is the acceptable conversions when binding an object. When you *have one*, you can only *use* the members of `Predicate` – Caleth May 09 '18 at 14:12

5 Answers5

21

For a Predicate<? super SomeClass> variable, you can assign a Predicate<SomeClass> instance, or a Predicate<Object> instance.

However, you can't pass an Object to the test() method of a Predicate<SomeClass>. You can only pass a SomeClass instance.

Therefore you can't pass an Object to the test() method of a Predicate<? super SomeClass>

Consider the following:

Predicate<URL> p1 = u -> u.getFile().isEmpty();
Predicate<? super URL> p2 = p1;

p2 is referring to a Predicate<URL>, so you can't pass a new Object() to its test() method.

In other words, in order for p.test(new Object()) to be accepted by the compiler, it must be valid for any Predicate that can be assigned to the Predicate<? super URL> p variable. Since the Predicate<URL> Predicate can be assigned to that variable, and its test() method cannot accept an Object, p.test(new Object()) cannot be accepted by the compiler.

BTW, in your specific example, you are creating a Predicate<URL>, and URL is a final class. Therefore, you should simply declare it as:

Predicate<URL> p = u -> u.getFile().isEmpty();

There's no reason for ? super or ? extends.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • 1
    I understand that it *should not* work, but I don't understand why it does not work. I'm missing something fundamental here probably. – lexicore May 09 '18 at 08:38
  • The code is not real/practical, I'm just trying to understand how ` super T>` works. – lexicore May 09 '18 at 08:41
  • `URL` is a final class is not a reason to remove `? super URL`, this is true only for the case of `? extends URL`. – marsouf May 09 '18 at 14:14
2

Consider the broader example:

Predicate<? super Integer> p;

In this situation, any of the following assignments is valid, right?

p = (Predicate<Integer>) i -> true; // but, only Integer can be applied
p = (Predicate<Number>)  n -> true; // Number and its sub-classes (Integer, Double...)
p = (Predicate<Object>)  o -> true; // any type

So, if in the end it is:

Predicate<? super Integer> p  = (Predicate<Integer>) i -> true;

Then, certainly, neither Number nor Object could not be applied to the predicate.

To guarantee type safety, a compiler allows only those types, which are valid for any of the possible assignments to the Predicate<? super Integer> - so, only Integer in this particular example.

Changing the lower bound from Integer to Number extends the boundaries respectively:

Predicate<? super Number> p  = (Predicate<Number>) n -> true;

Now, the predicate can be applied to the Number and any of its sub-classes : Integer, Double etc.

Summarizing

The only types which can be applied to the Predicate<? super SomeClass> without breaking a type safety guarantees: the lower bound itself and its sub-classes.

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
1

The Predicate already has a type. The type the predicate accepts can't be decided at the point when you try and call it. It has a type, and that type is some superclass of X; it's just unknown.

If I have a Predicate<Collection>, that could be referenced as a Predicate<? super List>. But that doesn't mean that it will accept any Object. The ? extends X generic type doesn't mean it will accept anything matching the given constraints. It means it is a predicate for some kind of unknown type that matches the given constraints.

Predicate<? super List> predicate = Collection::isEmpty;
predicate.test(new Object()); // Should this be valid? Clearly not
khelwood
  • 55,782
  • 14
  • 81
  • 108
  • *"It means it is a predicate for some kind of type matching the given constraints."* - but `Object` is a type which matches given constraints. – lexicore May 09 '18 at 08:32
  • But a `Predicate super X>` isn't a predicate for _any type_ matching the given constraints. It is a predicate for _some unknown type_ matching the given constraints. – khelwood May 09 '18 at 08:33
  • Sorry, I don't understand this. Can't `Object` be this "unknown type"? – lexicore May 09 '18 at 08:34
  • @lexicore Object means anthing. ;-) – Hello World May 09 '18 at 08:36
  • 1
    @lexicore The `Predicate` already _has_ a type. The type the predicate accepts can't be decided at the point when you try and call it. It has a type, and that type is some superclass of `X`; it's just unknown. – khelwood May 09 '18 at 08:37
  • @khelwood If the problem is that the type is unknown, why does it accept an instance of `URL` then? – lexicore May 09 '18 at 08:39
  • 1
    Because the type is unknown but _must_ be a superclass of URL. That's what `super URL` is for. Therefore it can be passed a URL. – khelwood May 09 '18 at 08:40
  • We're running in circles, sorry. *"Because the type is unknown but must be a superclass of URL. ... Therefore it can be passed a URL."* - `Object` satisfies this condition, why can't it be passen an instance of `Object`? – lexicore May 09 '18 at 08:42
  • @khelwood Does it means '? super X exclude Object' – Hello World May 09 '18 at 08:43
  • The object you pass into the predicate must be a subtype of the actual type of the predicate, because that's how subtyping works. The actual type of the predicate is _some unknown superclass of URL_. Therefore a URL is an acceptable parameter. And an Object is not, because there is no guarantee that the predicate's type is compatible with Object. – khelwood May 09 '18 at 08:49
1

The java tutorial pages provides some good information about upper / lower bounded wildcards when using generics.

It also provides some guidelines for using wildcards which can help decide which wildcard you should use:

An "In" Variable An "in" variable serves up data to the code. Imagine a copy method with two arguments: copy(src, dest). The src argument provides the data to be copied, so it is the "in" parameter. An "Out" Variable An "out" variable holds data for use elsewhere. In the copy example, copy(src, dest), the dest argument accepts data, so it is the "out" parameter.

Of course, some variables are used both for "in" and "out" purposes — this scenario is also addressed in the guidelines.

You can use the "in" and "out" principle when deciding whether to use a wildcard and what type of wildcard is appropriate. The following list provides the guidelines to follow: Wildcard Guidelines:

An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
An "out" variable is defined with a lower bounded wildcard, using the super keyword.
In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.

These guidelines do not apply to a method's return type. Using a wildcard as a return type should be avoided because it forces programmers using the code to deal with wildcards.

Chris
  • 958
  • 8
  • 19
0

This is quite a complex subject. These ? extends T and ? super T declarations are rather supposed to create a "matching" between classes.

If a class defines a method taking a Consumer, say something like Iterable<T>.foreach(), it defines this consumer to accept everything which can take a T. Thus, it defines it as forEach(Consumer<? super T> action). Why? Because an Iterator<Integer>'s foreach() can be called with a Consumer<Number> or even a Consumer<Object>. This is something which wouldn't be possible without the ? super T.

OTOH, if a class defines a method taking a Supplier, say something like addFrom, it defines this supplier to supply everything which is a T. Thus, it defines it as addFrom(Supplier<? extends T> supplier). Why? Because a ThisClass<Number>'s addFrom() can be called with a Supplier<Integer> or a Supplier<Double>. This is something which wouldn't be possible without the ? extends T.

Maybe a better example for the latter: List<E>.addAll() which takes a Collection<? extends E>. This makes it possible to add the elements of a List<Integer> to a List<Number>, but not vice versa.

glglgl
  • 89,107
  • 13
  • 149
  • 217