Why does 1 work and 2 not ?
1:
public List<? extends Integer> l1;
public List<? extends Number> l2 = l1;
2:
public List<U> l1;
public List<S> l2 = l1;
//where U and S have been previously defined as: **S extends Number,U extends Integer**
What is the difference between a wildcard bound and a type parameter bound? A wildcard can have only one bound, while a type parameter can have several bounds. A wildcard can have a lower or an upper bound, while there is no such thing as a lower bound for a type parameter.
In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.
A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.
Wildcard arguments means unknown type arguments. They just act as placeholder for real arguments to be passed while calling method. They are denoted by question mark (?). One important thing is that the types which are used to declare wildcard arguments must be generic types.
Generics are not covariant. For example:
List<Integer> l1;
List<Number> l2;
l1 = l2; // incompatible types
l2 = l1; // also incompatible
However, wildcarded types offer a way to express covariance:
List<? extends Integer> l1;
List<? extends Number> l2 = l1; //legal
l1
is being expressed as a List
of some unknown type that is or extends Integer
. Similarly, l2
is a List
of some type that is or extends Number
. Since Integer
extends Number
, the compiler knows assigning l1
to l2
must be okay.
This situation is different:
<S extends Number, U extends Integer> void someMethod() {
List<U> l1;
List<S> l2 = l1; //incompatible types
}
S
and U
are type parameters, meaning they are provided some specific type arguments by callers of someMethod
(or type inferrence). These type arguments could be a concrete type like Integer
or a wildcard capture.
While they are also bounded, this is different from using bounded wildcards like above. Type parameters are bounded at declaration - within the method body they are understood not to change. For example, let's say both S
and U
were resolved to Integer
by calling:
this.<Integer, Integer>someMethod();
In this case we can imagine the method body looks like this:
List<Integer> l1;
List<Integer> l2 = l1; // okay why not?
This would be legal, but we just happened to get lucky. There are many situations where it wouldn't be. For example:
this.<Double, Integer>someMethod();
Now we reimagine the method body:
List<Integer> l1;
List<Double> l2 = l1; // danger!
So you can see that a bounded type parameter is a something much different from a bounded wildcard, which allows different generic types to be covariantly "swapped in":
List<Integer> l1;
List<Double> l2;
List<? extends Number> l3;
l3 = l1;
l3 = l2;
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