Java Supplier<TYPE> vs Supplier<? extends TYPE>

815 Views Asked by At

I know it may have something to do with Generic and Covariant return types. But I can not make it through.

Given two classes where Apple extends Fruit.

public class TestReturnValue {
    public static Supplier<? extends Fruit> conFruitExt = Apple::new;
    public static Supplier<Fruit> conFruit = Apple::new;    // Compiles
    public static final Supplier<Fruit> supApple = supplyApple(); // Compile ERROR

    private static Supplier<Apple> supplyApple() {
        return Apple::new;
    }

    public static void main(String[] args) {
        Fruit fruitExt = conFruitExt.get();
        Fruit fruit = conFruit.get();

        System.out.println(fruitExt);
        System.out.println(fruit);
    }
}

What is the different between conFruitExt and conFruit? They both are constructor of Apple, and calling get() create apple instances.

Does the additional ? extends part of conFruitExt type make any difference?

Update1 2020/11/19

As suggestted answer says, question mark in generic by jonrsharpe, Supplier<Fruit> does not compile to Suppier<Apple>, so public static final Supplier<Fruit> supApple = TestReturnValue::supplyApple; does not compile.

But as method supplyApple()'s return value indicates Apple::new is type of Supplier<Apple>, why does public static Supplier<Fruit> conFruit = Apple::new compiles. Doesn't it assign a Supplier<Apple> instance to Supplier<Fruit>?

1

There are 1 best solutions below

2
Eugene On

By doing this:

public static Supplier<Fruit> conFruit = Apple::new;

you are creating a poly expression in Apple::new. These are inferred by the compiler in the context of usage. Think about it: if I give you just Apple::new - can you tell me what that is? A Supplier? Or a Provider (may be such a functional interface exists), etc? Thus these types are concluded from the surrounding context in which are used. It's like the compiler did:

public static Supplier<Fruit> conFruit = (Supplier<Fruit>) () -> new Apple();

which is perfectly legal (this is not what happens, but to make it easier for you to understand).

In the other method you have told what the return type is, explicitly:

private static Supplier<Apple> supplyApple() {...}

it is a Supplier<Apple>. Since generics are invariant, the second assignment fails.