1

I am dealing with the following puzzle.

class Product {}

static class Fruit extends Product implements Comparable<Fruit> {}

class Apple extends Fruit {}

Function<? super Fruit, ? extends Comparable> fx = Fruit::getCost;
Fruit fruit = new Fruit(1);
Product product = new Product();
fx.apply(product); <-- compile error?

What might be the reason fx not to accept Product object as argument? Product is parent of Fruit i.e. satisfies "? super Fruit" definition.

Function<? extends Fruit, ? extends Comparable> fx = Fruit::getCost;
Fruit fruit = new Fruit(1);
Apple apple = new Apple(2);
fx.apply(apple); <-- compile error?

Why apple would not be accepted as argument when its type is subtype of Fruit? Apple should satisfy "? extends Fruit".

kaya3
  • 47,440
  • 4
  • 68
  • 97
5rocks
  • 29
  • 2
  • What do your error messages say is the problem? – Scott Hunter Jan 21 '20 at 22:41
  • 1
    See [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/q/2723397/6395627). The `? super Fruit` means the `Function` _implementation_ is capable of handling `Fruit` **itself** or a supertype. That means only instances of `Fruit` can be consumed. Imagine if the compiler allowed you to pass a `Product` instance but the implementation expects a `Fruit`; that wouldn't work out well. As for why `? extends Fruit` doesn't work, that's because now the _implementation_ may expect any specific subtype of `Fruit`, not just any `Fruit` type. – Slaw Jan 21 '20 at 22:45
  • 1
    The fact that your function is defined as `Fruit::getCost` should already hint to you that there can’t be any construct allowing to pass a `Product` to that function, regardless of how it looks like. – Holger Jan 23 '20 at 15:58

3 Answers3

1

Let's think logically about what a Function<? super Fruit, ? extends Comparable> could be (for four examples):

  1. It could be a function which maps Fruit objects to something comparable.
  2. It could be a function which maps Product objects to something comparable.
  3. It could be a function which maps Comparable<Fruit> to something comparable.
  4. It could be a function which maps Object objects to something comparable.

In cases 2. and 4., a Product object meets the requirements to be a parameter of the function, but in cases 1. and 3. - where the function only accepts Fruit or Comparable<Fruit> - a Product is not a valid argument for the function. So the function might not be callable with your argument of type Product.


Let's think about what a Function<? extends Fruit, ? extends Comparable> could be (for four examples):

  1. It could be a function which maps Fruit objects to something comparable.
  2. It could be a function which maps Apple objects to something comparable.
  3. It could be a function which maps Banana objects to something comparable.
  4. It could be a function which maps some subclass of Apple to something comparable.

In cases 1. and 2., an Apple object meets the requirements to be a parameter of the function, but in cases 3. and 4. - where the function only accepts Banana or some subclass of Apple - an Apple is not a valid argument for the function. So the function might not be callable with your argument of type Apple.

kaya3
  • 47,440
  • 4
  • 68
  • 97
1

I wrote something and that helped me to understand the problem:

    Function<Fruit, ? extends Comparable> fx1 = Fruit::getCost;
    Function<? super Fruit, ? extends Comparable> fx2 = fx1;
    fx2.apply(new Product()); <-- COMPILER ERROR

Function fx2, which is fx1 knows/expects a Fruit. If the compiler allows Product this would be a problem.

    Function<Apple, ? extends Comparable> fx1 = Fruit::getCost;
    Function<? extends Fruit, ? extends Comparable> fx2 = fx1;
    fx2.apply(new Fruit(1));    <-- COMPILER ERROR

Function fx2, which is fx1 knows/expects a Apple. If the compiler allows Fruit this would be a problem.

5rocks
  • 29
  • 2
1

Perhaps this will clarify things:

Function<Fruit, Integer> costFunc = Fruit::getCost;
Function<? super Fruit, ? extends Comparable<?>> fx = costFunc;

Every object with a generic typing has exact type arguments. While you can refer to a Function as Function<? super Fruit, …>, there is no such thing as an object of that type. There is a Function<Fruit, …>, a Function<Product, …>, and a Function<Object, …>, but no existing object can actually be of a wildcard type (that is, a type which uses a question mark). The ? is only a way to tell the compiler that you aren’t sure what the object’s actual generic type is.

In the above code, the compiler sees that fx is declared as Function<? super Fruit, ? extends Comparable<?>>. The compiler doesn’t know what the actual generic type of the Function’s input parameter is, because you have told it that it’s some specific type which might be Fruit, or any class or interface inherited by Fruit.

So the compiler doesn’t know whether it’s safe to pass a Product to the apply method. What if fx holds a Function which requires a Fruit, not just any Product, as the first argument? Meaning, a Function<Fruit, …> (which is what the example shows)?

If you want to declare a Function which accepts Fruit or any supertype of Fruit, you would write Function<Object, …> without using any wildcard at all.

VGR
  • 40,506
  • 4
  • 48
  • 63