Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are Java wildcards more powerful than use-site variance?

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?

like image 239
gexicide Avatar asked Jul 17 '14 15:07

gexicide


People also ask

Why do we need wildcards in Java?

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.

What is the use of wildcard in generics?

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).

What is wild card in generics in Java?

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.


1 Answers

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 the swapLastTwo method, the type parameter to pass for E cannot be manually specified because the type hidden by ? cannot be named by the programmer. However, passing a Stack<?> 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 poped. 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.

like image 111
gexicide Avatar answered Oct 06 '22 00:10

gexicide