Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java wildcards vs type parameters

Tags:

java

generics

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**
like image 881
Razvan Avatar asked Apr 03 '13 10:04

Razvan


People also ask

What is the difference between a wildcard bound and a type parameter bound?

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.

What is wildcard types in Java?

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.

What is type parameter in Java?

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.

What is wildcard arguments in Java?

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.


1 Answers

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;
like image 61
Paul Bellora Avatar answered Sep 26 '22 09:09

Paul Bellora