views:

121

answers:

5

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;
    }
}
+1  A: 

Not a direct answer but i strongly recommend the reading of: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf to better understand generics.

Manuel Selva
+8  A: 

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).


A more generic 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:

  • Java generics is type invariant
  • There's a conversion from Integer to Number, but a List<Integer> is not a List<Number>
    • Similarly, a List<Integer> can be capture-converted by a List<?>, but a List<List<Integer>> is not a List<List<?>>
  • Using bounded wildcard, a List<? extends Number> can capture-convert a List<Integer>
    • Similarly, a 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!

Related questions

See also

polygenelubricants
+3  A: 

Although your generic type parameter contains a wildcard, it is not itself a wildcard. When assigning to a variable (Msg<T>) with a non-wildcard generic type T, the object being assigned must have exactly T as its generic type (including all generic type parameters of T, wildcard and non-wildcard). In your case T is Value<String>, which is not the same type as Value<?>.

What you can do, because Value<String> is assignable to Value<?>, is use the wildcard type:

Msg<? extends Value<?>> a = new Msg<Value<String>>();
ILMTitan
A: 

ILMTitan has a good solution, and if you don't want to make the class specific to Value you may as well use the base type instead of generics at this point because you'll be turning off a safety feature, but there is a way. You might even be able to pass a parameter to make this method more generic, but the key is "@SuppressWarnings".

@SuppressWarnings("unchecked")
Msg<Value<?>> convert()
{
    return (Msg<Value<?>>) this;
}
Peter DeWeese
If `this` is a `Msg<Value<String>>`, this wouldn't compile. It's not an unchecked cast. It's a straightup _ILLEGAL_ cast. It's like trying to cast a `String` to an `Integer`.
polygenelubricants
+2  A: 

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).

Ricky Clarkson