Jelle Fresen
Jelle Fresen

Reputation: 1946

How can I make different captures of a wildcard compatible?

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

Answers (1)

Ben Schulz
Ben Schulz

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

Related Questions