I have some code that returns a wildcard parameterized type. I'm trying to pass this to a parametric method, but I get a compiler error. Can someone explain to me why the types are not matched, and what the best way to fix this is?
static <L extends List<T>,T extends Number>
void useList(List<L> list) {}
public static void main(String[] args) {
List<List<? extends Number>> list = null;
useList(list);
}
Compile error:
demo/GenericsHell.java:75: error: method useList in class GenericsHell cannot be applied to given types;
useList(list);
^
required: List<L>
found: List<List<? extends Number>>
reason: inference variable L has incompatible bounds
equality constraints: List<? extends Number>
upper bounds: List<CAP#1>
where L,T are type-variables:
L extends List<T> declared in method <L,T>useList(List<L>)
T extends Number declared in method <L,T>useList(List<L>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
1 error
In my real code list
is being generated by a complex method that can return several types of "List" (actually a custom generic class). Is there another way to parameterize the useList function such that it will accept this in a type-safe way?
Edit 1
I am trying to reduce a large module of code into a concise, coherent question. From the answers I can see that the above code over-simplifies the precise problem I'm having. I'll try to restate with a more complex example, while better specifying my constraints.
First off, I'm not actually using List, but rather a more complex class with a double parameterization. I can't think of any standard classes that do this, so I'll define one for use in the example:
static class NumberLists<L extends List<T>,T extends Number> extends ArrayList<L> {}
I'll avoid reusing a type more than once, so it's easier to keep the levels straight. The useList() method really does need a double parameterization because it uses both types internally:
static <L extends List<T>,T extends Number>
Set<NumberLists<L,T>> useList(L list) {
NumberLists<L,T> nl = new NumberLists<L, T>();
nl.add(list);
return Collections.singleton(nl);
}
This framework works great as long as you have a concrete class:
// Everything works with a concrete class
List<Integer> intList = new ArrayList<Integer>();
// Use with parametric functions
Set<NumberLists<List<Integer>,Integer>> concreteSet = useList(intList);
// Access & use elements
NumberLists<List<Integer>,Integer> concreteNL = concreteSet.iterator().next();
concreteNL.add(intList);
The problem is that I have an input list that can be several different types:
static List<? extends Number> makeList() {
if(Math.random()<.5) {
return new ArrayList<Integer>();
} else {
return new ArrayList<Double>();
}
}
List<? extends Number> numList = makeList();
This breaks many of the patterns that were possible above.
// What is the type here? This is obviously an error
Set<NumberLists<? extends List<? extends Number>>,? extends Number> paramSet = useList(numList);
// Type should ideally be compatible with numList still
NumberLists<?,?> paraNL1 = paramSet.iterator().next();
// Captures don't match
paraNL1.add(numList);
So the problem is that I can't know the concrete class of numList at compile time. And it doesn't feel like I'm doing anything that's actually type-unsafe, since I know that the type of numList has to match the type returned by useList and so on.
I do have control over the type signatures of most parts of the code. However, I would prefer the following:
- It should work nicely and in a type-safe manner with concrete classes (e.g. intList)
- It will also receive input whose type can't be known specifically until runtime
- If casts or other unchecked operations are performed, they should happen near the construction of the input. Following operations should be checked.
For a complete (but non-compiling) java file, see https://gist.github.com/sbliven/f7babb729e0b1bee8d2dabe5ee979431. For the reason I'm interested in this question, see https://github.com/biojava/biojava/issues/354.