I have read often that Java wildcards are a concept that is more powerful than the concept of use-site variance. But in my understanding, the concept of Java wildcards is exactly equal to the concept of use-site variance.
So what is the difference between the two? Can you give a concrete example that is possible with Java wildcards but not with use site variance?
For example, the first answer in How does Java's use-site variance compare to C#'s declaration site variance? is one example where the claim of my question is made:
First off, if I remember correctly use-site variance is strictly more powerful than declaration-site variance (although at the cost of concision), or at least Java's wildcards are (which are actually more powerful than use-site variance).
However, the answer does not state what is the difference, only that there is one.
Edit:
A first difference I have found here (first paragraph on Page 112) seems to be that use-site variance completely disallows calling a method that has a type parameter in the wrong position while wildcards allow calling it with some types. E.g., you cannot call add
, on a List<? extends String>
. With Java wildcards, you can call add
on such a class, but you have to use null
as argument.For contravariance, one can call any method that returns a contravariant parameter but one has to assume that the return type is Object
. But is this the only difference?
Wildcards in Java are basically the question marks which we use in generic programming, it basically represents the unknown type. We use Java Wildcard widely in situations such as in a type of parameter, local variable, or field and also as a return type.
In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific).
The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.
After reading a lot on this topic, I seem to have found an answer in this paper of Altidor, Reichenbach, and Smaragdakis. The main addition that Java generics have in contrast to use-site variance is capture conversion which allows capturing the previously unknown type of a wildcard in a type parameter. This example from the paper explains it best:
One complication is that Java wildcards are not merely use-site variance, but also include mechanisms inspired by existential typing mechanisms. Wildcard capture is the process of passing an unknown type, hidden by a wildcard, as a type parameter in a method invocation. Consider the following method, which swaps the order the two elements at the top of a stack.
<E> void swapLastTwo(Stack<E> stack) { E elem1 = stack.pop(); E elem2 = stack.pop(); stack.push(elem2); stack.push(elem1); }
Although a programmer may want to pass an object of type
Stack<?>
as a value argument to theswapLastTwo
method, the type parameter to pass forE
cannot be manually specified because the type hidden by?
cannot be named by the programmer. However, passing aStack<?>
type checks because Java allows the compiler to automatically generate a name for the unknown type (capture conversion) and use this name in a method invocation.
I.e., in Java, we can call swapLastTwo()
using a Stack<?>
as input argument. The compiler then captures the ?
into a the type variable E
and therefore knows that we can call push
on the elements we just pop
ed. With use-site variance, we could not do this as the type system would lose the information that the element returned from pop
is of the type that push
anticipates.
Note that we have to use a type parameter to capture the type. Not doing so will make the type checker treat the type returned from pop
differently as the type that push
anticipates.
E.g., this does not compile in Java:
Stack<?> s = ...;
s.push(s.pop());
Here, the type of the element of s
will be captured into two different fresh type variables (called capture 1 of ?
and capture 2 of ?
in eclipse. The type checker treats these type variables as different and the code does not compile. By using a generic method we can capture the type of ?
into a named parameter that allows calling push
and pop
.
I am not sure if this is the only difference between Java wildcards and "usual" (whatever that is) use-site variance, but at least it seems to be a very notable difference.
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