I have two classes with nested generics. Is there a way to get rid of the
Type mismatch: cannot convert from Msg<Value<String>>
to Msg<Value<?>>
error ?
In the last assignment
public class Value<V> {
V val;
public Value(V val) {
this.val = val;
}
@Override
public String toString() {
return "" + val;
}
}
public class Msg<T> {
T holder;
public Msg( T holder) {
this.holder = holder ;
}
public String toString() {
return "" + holder;
}
public static void main(String[] args) {
Msg<Value<String>>strMsg = new Msg(new Value<String>("abc"));
// This is OK
Msg<?>objMsg = strMsg;
// Type mismatch: cannot convert from Msg<Value<String>> to Msg<Value<?>>
Msg<Value<?>>objMsg = strMsg;
}
}
The Java compiler won't let you cast a generic type across its type parameters because the target type, in general, is neither a subtype nor a supertype.
To use Java generics effectively, you must consider the following restrictions: Cannot Instantiate Generic Types with Primitive Types. Cannot Create Instances of Type Parameters. Cannot Declare Static Fields Whose Types are Type Parameters.
One restriction of generics in Java is that the type parameter cannot be a primitive type. To understand why primitive data types don't work, let's remember that generics are a compile-time feature, meaning the type parameter is erased and all generic types are implemented as type Object.
The polymorphism applies only to the 'base' type (type of the collection class) and NOT to the generics type.
Use the following:
Msg<? extends Value<?>> someMsg = strMsg;
The problem is that the ?
in Msg<Value<?>> objMsg
is NOT capable of capture conversion. It's not "a Msg
of Value
of some type. It's "a Msg
of Value
of ANY type".
This also explains why along with the declaration change, I've also renamed the variable to someMsg
. The Value
can't just be any Object
. It must belong to some type (String
in this example).
Let's consider a more generic example of a List<List<?>>
. Analogously to the original scenario, a List<List<?>>
can NOT capture-convert a List<List<Integer>>
.
List<List<Integer>> lolInt = null;
List<List<?>> lolAnything = lolInt; // DOES NOT COMPILE!!!
// a list of "lists of anything"
List<? extends List<?>> lolSomething = lolInt; // compiles fine!
// a list of "lists of something"
Here's another way to look at it:
Integer
to Number
, but a List<
Integer
>
is not a List<
Number
>
List<Integer>
can be capture-converted by a List<?>
, but a List<
List<Integer>
>
is not a List<
List<?>
>
List<? extends
Number
>
can capture-convert a List<
Integer
>
List<? extends
List<?>
>
can capture-convert a List<
List<Integer>
>
The fact that some ?
can capture and others can't also explains the following snippet:
List<List<?>> lolAnything = new ArrayList<List<?>>(); // compiles fine!
List<?> listSomething = new ArrayList<?>(); // DOES NOT COMPILE!!!
// cannot instantiate wildcard type with new!
List<List<? extends Number>>
List<Animal> animals = new ArrayList<Dog>()
?<E extends Number>
and <Number>
?My answer is similar to another, but hopefully is more clear.
List<List<?>> is a list of (lists of anything).
List<List<String>> is a list of (lists of strings).
The latter cannot be converted to the former because doing so would allow you to add a List<Number> to your List<List<String>>, which would clearly be broken.
Note that the rules for this don't change if you replace List with some type that doesn't have .add. Java would need declaration-site covariance and/or contravariance for that (like C#'s IEnumerable<out T> or Scala's List[+A]). Java only has use-site covariance and contravariance (? extends X, ? super X).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With