stacker
stacker

Reputation: 68942

Can't cast to to unspecific nested type with generics

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

Upvotes: 13

Views: 5995

Answers (5)

polygenelubricants
polygenelubricants

Reputation: 383726

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

Upvotes: 24

Ricky Clarkson
Ricky Clarkson

Reputation: 2939

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

Upvotes: 4

Peter DeWeese
Peter DeWeese

Reputation: 18333

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

Upvotes: 0

ILMTitan
ILMTitan

Reputation: 11017

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>>();

Upvotes: 3

Manuel Selva
Manuel Selva

Reputation: 19050

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.

Upvotes: 1

Related Questions