I’m trying to make a generic class with one type parameter, class MyClass<E>, that has a class variable of a second generic class with two type parameters, SecondClass<V, E>. Since for my code it really doesn’t matter what the type of V is, I declare the type of that variable as SecondClass<?, E> var. At some point in the implementation of MyClass I call a method on var that returns a V, public V foo(E e), and then I pass this object of type V to another method of var, public int bar(V v). However, this doesn’t compile because of reasons I only vaguely understand, but I believe it is explained in here.
Apparently, the capture-of-? returned by foo is different from the the capture-of-? required by bar. But why? Whatever the actual type of V is, it must be the same for both methods, since they are invoked on the same instance. What am I missing here?
Ultimately, what I would like to know is this: what do I need to change in order to make the code compile, without adding V to the type parameters of MyClass? (I don’t want to enforce users of MyClass to specify the type of V since it shouldn’t matter)
To give you a more concrete example, here’s a simplified version of what I’m working on. As you may already have guessed by the type parameters, it concerns graphs. MyClass translates to EdgePainter and SecondClass translates to Graph. With this code, the compile error is in the first line of EdgePainter.getColor(E).
class Graph<V, E>
{
public V getTarget(E edge)
{
return null;
}
public int getInDegree(V vertex)
{
return 0;
}
}
class EdgePainter<E>
{
private static final Color COLOR_FOR_MANY_EDGES = Color.RED;
private static final Color COLOR_FOR_FEW_EDGES = Color.BLUE;
private Graph<?, E> graph;
public EdgePainter(Graph<?, E> aGraph)
{
graph = aGraph;
}
public Color getColor(E edge)
{
// My compiler says:
// The method getInDegree(capture#3-of ?) in the type
// Graph<capture#3-of ?,E> is not applicable for the arguments
// (capture#4-of ?)
int degree = graph.getInDegree(graph.getTarget(edge));
if (degree > 10)
return COLOR_FOR_MANY_EDGES;
else
return COLOR_FOR_FEW_EDGES;
}
}
You can capture the wildcard by invoking a generic method.
This is a typical scenario. You need a type argument for the implementation, but want to hide it from API users. If many methods are affected it can be helpful to define a separate, perhaps nested, class
EdgePainterInternal. This internal implementation has the second type parameterGand the publicly visible implementationEdgePainterdelegates all calls to an instance ofEdgePainterInternal.