gstackoverflow
gstackoverflow

Reputation: 37106

How to instantiate generics using wild card?

Lets research some generic instantion situation using wild card:

1

This code

List<?> list = new ArrayList<?>();

generates following error:

required: class or interface without bounds
found:    ?

2

But this

List<?> list = new ArrayList< Set<?> >();

compiles succesfully.

3

and this:

List<Set<?>> list = new ArrayList< Set<?> >();

compiles succesfully too.

4

but this:

List<Set<Map<?,?>>> list = new ArrayList< Set<Map<String,String>> >();

generates

required: List<Set<Map<?,?>>>
found:    ArrayList<Set<Map<String,String>>>

5

List<Set<?>> list = new ArrayList< HashSet<?> >();

generates

required: List<Set<?>>
found:    ArrayList<HashSet<?>>

I am very confusing about these outputs.

I see following regularity:

I can replace ? from left part on right part only on first level and types should be same inside <> and just ? and ? is forbid.

But I don't understand why?

Can you provide common rules how to instantiate generics using wild card?

Upvotes: 6

Views: 2533

Answers (2)

MattPutnam
MattPutnam

Reputation: 3017

  1. You can't instantiate with a wildcard. You have to specify the type. "?" isn't a type.
  2. This is okay, because you're giving List<?> a type, which is Set<?>. Set<?> is a type.
  3. Correct. ArrayList<T> is a subtype of List<T>.
  4. This is where things get ugly. The right hand is not a subtype of the left hand, because of the screwy way that Java deals with type parameters. The type parameters have to match exactly, or you have to use covariance/contravariance (the <A extends B> or <A super B> stuff). List<String> is not a subtype of List<Object>. It is a subtype of List<? extends Object> or just List<?>.
  5. Same as above. If you had declared it as List<? extends Set<?>> it would work.

Upvotes: 2

rgettman
rgettman

Reputation: 178313

  1. You cannot use wildcards to instantiate a type directly. The type argument needs to be an actual type when instantiating, so this generates a compiler error.

Code:

List<?> list = new ArrayList<?>();
  1. The following compiles successfully.

Code:

List<?> list = new ArrayList< Set<?> >();

You can use a wildcard as a generic type parameter to a type argument, e.g Set<?>, a set of anything. Also, any type argument will match the wildcard ? on the left.

  1. This compiles successfully also:

Code:

List<Set<?>> list = new ArrayList< Set<?> >();

The type arguments match, and ? isn't used directly as in (1) above.

  1. The following doesn't compile:

Code:

List<Set<Map<?,?>>> list = new ArrayList< Set<Map<String,String>> >();

This is because even though a Map<String, String> is a Map<?, ?>, a List<Set<Map<String, String>>> is not a List<Set<Map<?, ?>>>. Java generics are invariant, meaning that type parameters must match; the "is-a" relationship must be specified explicitly with wildcards in upper bounds. E.g. this change compiles, by introducing upper bound wildcards on the left side.

List<? extends Set<? extends Map<?,?>>> list = new ArrayList< Set<Map<String,String>> >();
  1. This following doesn't compile for the same reason as in (4) above:

Code:

List<Set<?>> list = new ArrayList< HashSet<?> >();

Even if a HashSet<?> is a Set<?>, because of Java's invariant generics, an ArrayList<HashSet<?>> is not a List<Set<?>>. Introducing wildcards on the left as upper bounds works here too:

List<? extends Set<?>> list = new ArrayList< HashSet<?> >();

Upvotes: 4

Related Questions