1

I have a very basic code to reproduce the issue:

public class Main {
  public static <T extends Number> void bar(Wrapper<List<T>> wrapper) {
    // do something
  }

  public static void foo(Wrapper<List<? extends Number>> wrapper) {
    // do something
  }

  public static <T extends Number> void bar2(List<T> aList) {
    // do something
  }

  public static void foo2(List<? extends Number> aList) {
    // do something
  }

  public static void main(String[] args) {
    List<Integer> aList = Collections.singletonList(1);
    foo2(aList); // compiles
    bar2(aList); // compiles

    Wrapper<List<Integer>> wrapper = new Wrapper<>(Collections.singletonList(1));
    foo(wrapper); // not compiles
    bar(wrapper); // compiles
  }

  private static class Wrapper<T> {
    private final T t;

    public Wrapper(T t) {
      this.t = t;
    }
  }
}

So the question is why javac gives an error when I try to compile the code:

Main.java:26: error: method foo in class Main cannot be applied to given types;
    foo(wrapper); // not compiles
    ^
  required: Wrapper<List<? extends Number>>
  found: Wrapper<List<Integer>>
  reason: argument mismatch; Wrapper<List<Integer>> cannot be converted to Wrapper<List<? extends Number>>
1 error
Tom
  • 16,842
  • 17
  • 45
  • 54
Ivan
  • 837
  • 6
  • 15
  • It doesn't like the wildcard definition. declare it the same way as `bar`. – escape-llc Mar 07 '17 at 10:27
  • Possible duplicate of [Is List a subclass of List? Why aren't Java's generics implicitly polymorphic?](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p) – Tom Mar 07 '17 at 10:41
  • The explanation from the linked question partly answers on my question, however I believe that the questions are slightly different and it would be usefull to keep this one. – Ivan Mar 07 '17 at 11:13

3 Answers3

2

Because Java generics are not covariant.

In your case

Wrapper<List<Integer>>

instance cannot be used as

Wrapper<List<? extends Number>>

parameter because

List<Integer>

does not extend

List<? extends Number>
Dmitry Gorkovets
  • 2,208
  • 1
  • 10
  • 19
  • Because "bar" receives "Wrapper>" where T will be Integer which extends Number. – Dmitry Gorkovets Mar 07 '17 at 11:00
  • So the type of T is being resolved prior to type of Wrapper's list type, whereas the original foo case the type of Wrapper's list is not being resolved at all, right? – Ivan Mar 07 '17 at 11:11
2

Wrapper<List<Integer>> is not a subtype of Wrapper<List<? extends Number>>.

Use Wrapper<? extends List<? extends Number>>.


To see why you can't pass Wrapper<List<Integer>> to foo, consider the following: I've replaced Wrapper with List, but it's the same thing from a type safety point of view.

List<List<? extends Number>> list = new ArrayList<>();
List<List<Integer>> intList = new ArrayList<>();

So, you can add something to list:

List<Double> doubles = new ArrayList<>(Arrays.asList(0.0));
list.add(doubles);

But, if you were able to write this assignment:

list = intList;

Then calling list.add(doubles) would mean there would then be a List<Double> in intList. This is not type safe, and thus forbidden.

Ideone demo

However, if list has type List<? extend List<? extends Number>>, then you can't invoke add on it, so you can't get into this situation, so that would be safe.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • the same result :) – Ivan Mar 07 '17 at 10:43
  • public static void foo(Wrapper extends List extends Number>> wrapper) and the method invocation Wrapper> wrapper = new Wrapper<>(Collections.singletonList(1)); foo(wrapper); – Ivan Mar 07 '17 at 10:45
  • [Yes, that works](http://ideone.com/Iy8gNW). Try clicking on the links I'm posting, they show it compiles just fine. – Andy Turner Mar 07 '17 at 10:46
1

Because List<Integer> is not a subclass of List<? extends Number>

c0der
  • 18,467
  • 6
  • 33
  • 65