Richard
Richard

Reputation: 112

Generic types and wildcards in nested lists

I am trying to write a method that takes any List of List as a parameter - regardless of the type of the nested list.
For a method that takes a single list of any type, the wildcard type works fine (see example1). But this concept does not extends to a list of lists. As can be see in example2 the method then only accepts parameters of type List<List<?>> but not of a specific type such as List<List<Integer>>. Example3 uses a type parameter T and accepts List<List<Integer>> but no longer accepts List<List<?>>. Why is this and how can I write a method that accepts both List<List<?>> and List<List<Integer>>?

public void test()
{
    List<Integer> list1;
    List<?> list2;
    
    example1(list1); // Valid
    example1(list2); // Valid
    
    List<List<Integer>> listOfLists1;
    List<List<?>> listOfLists2;
    
    example2(listOfLists1); // Error
    example2(listOfLists2); // Valid
    
    example3(listOfLists1); // Valid
    example3(listOfLists2); // Error
}

public void example1(List<?> list) {}

public void example2(List<List<?>> listOfLists) {}

public <T> void example3(List<List<T>> listOfLists) {}

Upvotes: 2

Views: 317

Answers (2)

newacct
newacct

Reputation: 122439

example2 doesn't work because List<List<Integer>> is not a subtype of List<List<?>> even though List<Integer> is a subtype of List<?>, similar to how List<String> is not a subtype of List<Object> even though String is a subtype of Object. Basically, when the type is not directly parameterized by a wildcard, the type parameter must match exactly -- List<Integer> and List<?> do not match exactly. (There is a wildcard deep inside there, but it is as part of the concrete type List<?>, not a wildcard at the top level.)

For it to be able to take different type parameters, it needs to have a wildcard at the top level:

public void example4(List<? extends List<?>> listOfLists) {}

Upvotes: 4

Michael K.
Michael K.

Reputation: 71

That works for me:

List<List<Integer>> listOfLists1;
List<List<?>> listOfLists2;

public TestKlasse() {
    init();
}

public void init(){
    listOfLists1 = new ArrayList();
    listOfLists2 = new ArrayList();
    listOfLists1.add(Arrays.asList(1,2,3,4,5));
    listOfLists2.add(Arrays.asList("a","a",2,4,"5"));
    test((Collection)listOfLists1);
    test((Collection)listOfLists2);
}

private void test(Collection<Collection<?>> list){
    list.forEach(e -> {
        System.out.println(e.toString());
    });
}

The result:

[1, 2, 3, 4, 5]
[a, a, 2, 4, 5]

I hope that this solution fit your expectation.

Upvotes: 1

Related Questions