开发者

Java generics type mismatch

I have an abstract class defined as:

public abstract class TCV<T extends MF> {
    public Map<String, MVR<T>> 开发者_开发百科operation() {
        ...
    }
}

main code:

TCV<? extends MF> o = new TCVConcrete();
Map<String, MVR< ? extends MF>> map = o.operation();

Error from eclipse:

Type mismatch: cannot convert from Map<String,MVR<capture#5-of ? extends MF>> to Map<String,MVR<? extends MF>>

EDIT

public class TCVConcrete extends TCV<MFV2> {
}

public class MFV2 extends MF {
}


The main problem is because you can't cast Box<Tiger> to Box<Cat>, why? You can think it as,

A Box<Cat> can contain cats and tigers, but a Box<Tiger> can contain only tigers. If you cast a Box<Tiger> to Box<Cat>, and later you throw a BlackCat into the Box<Tiger>:

Box<Cat> catBox = tigerBox;
catBox.throwHere(new BlackCat());

and then, the tigerBox is corrupted.

Let me reword your question as:

public abstract class Company<T extends Cat> {
    public Box<T> getFavBox() {
        // ...
    }
    public Set<Box<T>> getBoxes() {
        // ...
    }
}

Company<? extends Cat> o = new ETCatCompany();
Box<? extends Cat> boxes = o.getFavBox();        // ok
Set<Box<? extends Cat>> boxes = o.getBoxes();    // error

As you see, o.getFavBox() will work but o.getBoxes() not, why?

Because, you could not throw any concrete Cat into Box<? extends Cat>, which may possibly corrupt the unknown box. However, you can throw a Box<BlackCat> into Set<Box<? extends Cat>>, which may in turns corrupt a Set<Box<Tiger>>.

Or, you may think it as:

Cat ^ Tiger => Cat
Box<Cat> ^ BigBox<Cat> => Box<Cat>
Box<Cat> ^ Box<Tiger> => Box<? extends Cat>
Box<Cat> ^ BigBox<Tiger>
    => (Box<Cat> ^ BigBox<Cat>) ^ (BigBox<Cat> ^ BigBox<Tiger>)
    => Box<Cat> ^ BigBox<? extends Cat>
    => Box<? extends Cat> ^ BigBox<? extends Cat>
    => Box<? extends Cat>

Set<Box<Cat>> ^ Set<BigBox<Cat>> => Set<? extends Box<Cat>>
Set<Box<Cat>> ^ Set<Box<Tiger>> => Set<? extends Box<? extends Cat>>
Set<Box<Cat>> ^ Set<BigBox<Tiger>> => Set<? extends Box<? extends Cat>>

? extends Cat ^ ? extends Tiger => ? extends Cat
Box<? extends Cat> ^ BigBox<? extends Cat> => Box<? extends Cat>
Box<? extends Cat> ^ Box<? extends Tiger> 
    => ? extends Box<? extends (? extends Cat)>
    => ? extends Box<? extends Cat>

Set<Box<? extends Cat>> ^ Set<BigBox<? extends Cat>> 
    => Set<? extends Box<? extends Cat>>

Set<Box<? extends Cat>> ^ Set<Box<? extends Tiger>>     // You are here.
    => Set<? extends Box<? extends Cat>>

Set<Box<? extends Cat>> ^ Set<BigBox<? extends Tiger>> 
    => Set<? extends Box<? extends Cat>>

Or more likely in your case, give several operation() calls:

Map<String, MVR<?1 extends MF>> x1 = o.operation();
Map<String, MVR<?2 extends MF>> x2 = o.operation();
...
Map<String, MVR<?n extends MF>> x2 = o.operation();

Then, what's the common type of all?

x1 ^ x2 ^ ... ^ xn
    => Map<String, MVR<?1 extends MF>> ^ Map<String, MVR<?2 extends MF>> ^ ...
    => Map<String, ? extends MVR<? extends MF>> ^ ...
    ...
    => Map<String, ? extends MVR<? extends MF>>

Well, it's definitely nothing wrong with two wildcards, it's about mathematical induction...


The problem here is that you have two different generic definitions that could be the same:

TCV<? extends MF> o = new TCVConcrete();
    ^^^^^^^^^^^^
Map<String, MVR< ? extends MF>> map = o.operation();
                ^^^^^^^^^^^^^^

While these two could point to the same type, they could also point to two different sub-types of MF. The compiler doesn't like that.

You can solve that with a type variable in a dedicated method.
This should work:

public void <T extends MF> doSomething(){
    TCV<T> o = new TCVConcrete();
    Map<String, MVR<T>> map = o.operation();
}


There's no way for the generics system to understand that the indicated generic types in the two lines (when applied to a concrete implementation) actually refer to the same concrete type when the compiler does its business.

TCV<? extends MF> o = new TCVConcrete();
Map<String, MVR< ? extends MF>> map = o.operation();

In the simple case of Serializable, there are many classes implementing Serializable, so one statement could mean one class, and another statement could mean another, thus this will not compile. You need to create a type scope at the top of the file, and then use the same type for both lines, or implement a subclass of MF and use this for both lines, rather than leaving it as a floating generic type.


The problem is that you're using two ? wildcards, which basically say "MF or some unspecified subclass of MF". But that could mean two different subclasses of MF; and that's why the types for the assignment don't match.

What you probably want is not to use the wildcards at all. This should work:

TCV<MF> o = new TCVConcrete();
Map<String, MVR<MF>> map = o.operation();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜