简体   繁体   中英

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;
    }

}

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM