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();
精彩评论