问题
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?
回答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<?>();
- 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.
- This compiles successfully also:
Code:
List<Set<?>> list = new ArrayList< Set<?> >();
The type arguments match, and ?
isn't used directly as in (1) above.
- 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>> >();
- 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<?> >();
回答2:
- You can't instantiate with a wildcard. You have to specify the type. "?" isn't a type.
- This is okay, because you're giving
List<?>
a type, which isSet<?>
.Set<?>
is a type. - Correct.
ArrayList<T>
is a subtype ofList<T>
. - 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 ofList<Object>
. It is a subtype ofList<? extends Object>
or justList<?>
. - Same as above. If you had declared it as
List<? extends Set<?>>
it would work.
来源:https://stackoverflow.com/questions/26146371/how-to-instantiate-generics-using-wild-card