22

I've noticed JLS talks of 5.1.10 Capture Conversion, but I fail to understand what they are.

Can anyone explain them to me/give examples?

John Assymptoth
  • 8,227
  • 12
  • 49
  • 68
  • No examples other than in the discussion immediately below? – Tom Hawtin - tackline Dec 13 '10 at 17:57
  • That's true, they provide an example, but from it, I couldn't understand what capture conversions are. Did you understand the example? If so, could you kindly explain to me what they are. Thanks. – John Assymptoth Dec 13 '10 at 18:14
  • Wildcard Capture and Helper Methods https://docs.oracle.com/javase/tutorial/java/generics/capture.html Basically: given a `List> i`, the helper method converts the captured wildcard into a known type `` in the helper method, so that now you can invoke a method `set()` of `List l` that requires a known type. – Nor.Z Jan 26 '22 at 10:34

2 Answers2

27

Capture conversion was designed to make wildcards (in generics), ? useful.

Suppose we have the following class:

public interface Test<T> {
    public void shout(T whatever);
    public T repeatPreviousShout();

}

and somewhere on our code we have,

public static void instantTest(Test<?> test) {
    System.out.println(test.repeatPreviousShout());
}

Because test is not a raw Test and since repeatPreviousShout() in "hindsight" returns a ?, the compiler knows that there's a T that serves as a type parameter for Test. This T is for some unknown T so the compiler erases the unknown type (for wildcard, it replaces with Object)., hence repeatPreviousShout() returns an Object.

But if we had,

public static void instantTest2(Test<?> test) {
    test.shout(test.repeatPreviousShout());
}

The compiler would give us an error of something like Test<capture#xxx of ?> cannot be applied (where xxx is a number, e.g. 337).

This is because the compiler tries to do the type safety check on shout() but since it received a wildcard, it doesn't know what T represents, hence it creates a placeholder called capture of.

From here (Java theory and practice: Going wild with generics, Part 1), it clearly states:

Capture conversion is what allows the compiler to manufacture a placeholder type name for the captured wildcard, so that type inference can infer it to be that type.

Hope this helps you.

Adooo
  • 640
  • 6
  • 22
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • One additional example: http://stackoverflow.com/questions/4449611/can-anyone-give-me-an-example-where-the-eclipse-jdt-creates-an-ast-with-a-captur – John Assymptoth Dec 18 '10 at 04:50
  • @John, if you read the article (link provided above from IBMWorks), you'll see the example you gave comes from there. – Buhake Sindi Dec 18 '10 at 07:02
4

A parameterized type involving wildcard type arguments is really a union type. For example

List<? extends Number> = Union{ List<S> | S <: Number }

In 2 cases, instead of using List<? extends Number>, Java uses the captured version List<S>, where S is a just-created type variable with upper bound Number.

(1) http://java.sun.com/docs/books/jls/third_edition/html/expressions.html

To narrow the type of an expression. If an expression's type is List<? extends Number>, we know for sure that the runtime type of the object is actually a List<S> for some concrete type S (S <: Number>). So compiler uses List<S> instead, to perform more accurate type analysis.

Capture conversion is applied to each expression individually; this leads to some dumb results:

<T> void test1(List<T> a){}
<T> void test2(List<T> a, List<T> b){}

List<?> x = ...;
test1(x);    // ok
test2(x, x); // error

(2) http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.2

In subtype checking A :< B where A involves wildcard arguments. For example,

List<? extends Number>  :< B
<=>
Union{ List<S> | S <: Number}  :< B
<=>
List<S> :< B, for all S <: Number

So in effect, we are checking the captured version of type A

irreputable
  • 44,725
  • 9
  • 65
  • 93