Reputation: 1946
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;
}
}
Upvotes: 1
Views: 73
Reputation: 6181
You can capture the wildcard by invoking a generic method.
public Color getColor(E edge) {
return getColorInternal(graph, edge);
}
private <X> Color getColorInternal(Graph<X, E> g, E e) {
int degree = g.getInDegree(g.getTarget(e));
// ...
}
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 parameter G
and the publicly visible implementation EdgePainter
delegates all calls to an instance of EdgePainterInternal
.
Upvotes: 3