St.Antario
St.Antario

Reputation: 27385

Understanding a captured type in Java (symbol '?')

What is ?. Is it related to implementation details of the Java compiler or the type is defined in the JLS.

For instance,

public interface RecipientTypeVisitor<ReturnType> {
    public ReturnType visit(RecipientSetType t);
}

public class RecipientSetType extends RecipientType{

    public Integer accept(RecipientTypeVisitor<?> visitor){ //Error:
        return visitor.visit(this);             //Cannot convert capture of #1 to Integer
    }
}

But if we write this:

public interface RecipientTypeVisitor<ReturnType> {
    public ReturnType visit(RecipientSetType t);
}

public class RecipientSetType extends RecipientType{

    public Object accept(RecipientTypeVisitor<?> visitor){ //Ok:
        return visitor.visit(this);             //this implies tha the capture of #1 is 
                                     //a subtype of Object as any refrence type in Java.
    }
}

That's all I can say about a captured type. So what is it actually?

Upvotes: 4

Views: 2946

Answers (5)

Max
Max

Reputation: 529

You have some choices :

  1. you have to replace ? by Integer in the signature of accept method.
  2. you could parametrize your method accept too :

public <T> T accept(RecipientTypeVisitor<T> visitor) {...}

this indicates your method uses some generics and links some of its components.

Upvotes: 3

Lii
Lii

Reputation: 12112

The capture of a wildcard type is a type that is used by the compiler represent the type of a specific instance of the wildcard type, in one specific place.

Example: Take for example a method with two wildcard parameters, void m(Ex<?> e1, Ex<?> e2). The declared types of e1 and e2 are written exactly the same, Ex<?>. But e1 and e2 could have different and incompatible runtime types.

The type checker must not consider the types equal even if they are written the same way. Therefore, during compilation the type parameters of e1 and e2 are given specific types, new ones for each place they are used. These new types are called the capture of their declared types.

The capture of a wildcard is an unknown, but normal and concrete type. It can be used the same way as other types.

A technical description of this can be found in the JLS:

5.1.10. Capture Conversion

Let G name a generic type declaration (§8.1.2, §9.1.2) with n type parameters A1,...,An with corresponding bounds U1,...,Un.

There exists a capture conversion from a parameterized type G (§4.5) to a parameterized type G, where, for 1 ≤ i ≤ n :

  • If Ti is a wildcard type argument (§4.5.1) of the form ?, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is the null type (§4.1).
  • ...

We can apply this to your example:

public Integer accept(RecipientTypeVisitor<?> visitor){ //Error:
    return visitor.visit(this);             //Cannot convert capture of #1 to Integer
}

A capture of the type parameter of RecipientTypeVisitor is introduced during compilation. The captured type parameter is concrete but totally unknown (the JLS calls this a "fresh type variable"), and certainly not convertible to Integer.

There is no way to denote the capture of a wildcard type directly, so you can't declare variables of that type or do much with it. You can however get a name for it indirectly by calling a generic method with it as a parameter. The JLS section I cited has a nice example of this further down:

public static void reverse(List<?> list) { rev(list); }
private static <T> void rev(List<T> list) {
    List<T> tmp = new ArrayList<T>(list);
    for (int i = 0; i < list.size(); i++) {
        list.set(i, tmp.get(list.size() - i - 1));
    }
}

Upvotes: 3

ZhongYu
ZhongYu

Reputation: 19682

(This is from another answer of mine, but it is more suitable for this question, explaning wildcard itself)

A wildcard ? is not a type. It is a type argument. The syntax though is very deceiving (by design).

Let's use a different syntax - if there's any 1st-level wildcard, use {} instead of <>, e.g.

List{?},  Map{String, ? extends Number}

The meaning of {?} is to declare a union type

List{? extends Number}  ==  union of List<Number>, List<Integer>, List<Long>, ....

It's easy to see that, List<Integer> is a subtype of List{? extends Number}; and
List{? extends Number} is a subtype of List{? extends Object}

In our syntax, <> is reserved for substituting type vars with types. So we write
List<String>, etc. It's easy to understand their meaning - just replace T's with String in the source code of List, we get a good-old plain class.

    interface List<String>
        String get(int)

This cannot be done for wildcard - it makes no sense

    interface List<?>
        ? get(int)

So it is not allowed to new ArrayList{?}(), or class MyList implements List{?}

So, how can we use List{?}? What methods we can call on it?

When the type of an expression is a List{?}, we know that it is an object, and the object must belong to a subclass of List<x> for some unknown type x. This is wildcard capture

obj is a List{?}  =>  obj is a List<x>, where x a subtype of Object.

Even though the exact type of x is unknown at compile time, we can still do the substitution

    interface List<x>
        x get(int)

so we can make sense of the call obj.get(0); it returns x, and x is a subtype of Object; so we can assign the return value to an Object.

Upvotes: 2

Muhammad Suleman
Muhammad Suleman

Reputation: 2922

The char ? is a wildcard meaning you don't know the type. its doesn't mean its type is Object its by definition undefined but in Java you can hold any type of object in Object type.

ArrayList<?> list1 = new Arraylist<?>(); // list of undefined objects

You cannot add objects in list1, but after casting you can.

ArrayList<Object> list2 = new Arraylist<Object>(); // list of objects of type Object

You can add objects in list2 of any type

Upvotes: 1

aalku
aalku

Reputation: 2878

Every class extends from Object so the typecast from ? to Object is safe. To get the same with Integer you can use RecipientTypeVisitor<? extends Integer> but since Integer is a final class that is useless, Integer cannot be subclassed. It is equivalent to RecipientTypeVisitor<Integer>.

Why do you want this?

You can check the official tutorial here.

The most important thing to know:

In generic code, the question mark (?), called the wildcard, represents an unknown type.

(...)

In some cases, the compiler infers the type of a wildcard. For example, a list may be defined as List but, when evaluating an expression, the compiler infers a particular type from the code. This scenario is known as wildcard capture.

For the most part, you don't need to worry about wildcard capture, except when you see an error message that contains the phrase "capture of".

Upvotes: 1

Related Questions