Wildcard (Java)

From Wikipedia, the free encyclopedia
Jump to: navigation, search

The wildcard ? in Java is a special actual parameter for the instantiation of generic (parameterized) types. It can be used for the instantiation, not in the definition of a generic unit. Thus, a wildcard is a form of use-site variance annotation (contrast this with the definition-site variance annotations found in C# and Scala). This article summarizes the most important rules for its use.

Covariance for generic types[edit]

Unlike arrays (which are covariant in Java), different instantiations of a generic type are not compatible with each other, not even explicitly: With the declaration Generic<Supertype> superGeneric; Generic<Subtype> subGeneric; the compiler would report a conversion error for both castings (Generic<Subtype>)superGeneric and (Generic<Supertype>)subGeneric.

This incompatibility may be softened by the wildcard if ? is used as an actual type parameter: Generic<?> is the abstract supertype for all instantiations of the generic type. It means, no objects of this type may be created, only variables. The usage of such a variable is to refer to instantiations of Generic with any actual type parameter.

Wildcard as parameter type[edit]

In the body of a generic unit, the (formal) type parameter is handled like its upper bound (expressed with extends; Object if not constrained). If the return type of a method is the type parameter, the result (e.g. of type ?) can be referenced by a variable of the type of the upper bound (or Object). In the other direction, the wildcard fits no other type, not even Object: If ? has been applied as the formal type parameter of a method, no actual parameters can be passed to it. It can be called only by casting the wildcard reference:

class Generic <T extends UpperBound> {
	private T t;
	void write(T t) {
		this.t = t;
	}
	T read() {
		return t;
	}
}
...
Generic<?> wildcardReference;
UpperBound ub = wildcardReference.read(); // Object would also be OK
wildcardReference.write(new Object()); // type error
((Generic<UpperBound>)wildcardReference).write(new UpperBound()); // OK

Bounded Wildcards[edit]

A bounded wildcard is one with either an upper or a lower constraint. Not only the formal type parameters in the generic unit, but also the wildcard can be (further) constrained if one doesn't want to be compatible with all instantiations:

Generic<? extends SubtypeOfUpperBound> referenceConstrainedFromAbove;

This reference can hold any instantiation of Generic with an actual type parameter of SubtypeOfUpperBound's subtype. A wildcard that does not have a constraint is effectively the same as one that has the constraint extends Object, since all types implicitly extend Object. A constraint with a lower bound

Generic<? super SubtypeOfUpperBound> referenceConstrainedFromBelow;

can hold instantiations of Generic with any supertype (e.g. UpperBound) of SubtypeOfUpperBound. (Such a wildcard still has an implicit upper bound of Object.)

It is even possible to constrain a reference's compatibility from both sides: from above by a generic class or method definition (<SubtypeOfUpperBound extends UpperBound>), or from below by the reference declaration (<? super SubtypeOfUpperBound>).

Object Creation with Wildcard[edit]

No objects may be created with a wildcard type parameter: new Generic<?>() is forbidden because Generic<?> is abstract. In practice, this is unnecessary because if one wanted to create an object that was assignable to a variable of type Generic<?>, one could simply use any arbitrary type (that falls within the constraints of the wildcard, if any) as the type parameter.

On the other hand, an array object that is an array of a parameterized type may be created only by an unconstrained (i.e. with a wildcard type parameter) type (and by no other instantiations) as the component type: new Generic<?>[20] is correct, while new Generic<UpperBound>[20] is prohibited.

An example of using a wildcard in List's instantiation is contained in the article Generics in Java.

Example: Lists[edit]

In the Java Collections Framework, the class List<MyClass> represents an ordered collection of objects of type MyClass. Upper bounds are specified using extends: A List<? extends MyClass> is a list of objects of some subclass of MyClass, i.e. any object in the list is guaranteed to be of type MyClass, so one can iterate over it using a variable of type MyClass

public void doSomething(List<? extends MyClass> list) {
  for(MyClass object : list) { // OK
    // do something
  }
}

However, it is not guaranteed that one can add any object of type MyClass to that list:

public void doSomething(List<? extends MyClass> list) {
  MyClass m = new MyClass();
  list.add(m); // Compile error
}

The converse is true for lower bounds, which are specified using super: A List<? super MyClass> is a list of objects of some superclass of MyClass, i.e. the list is guaranteed to be able to contain any object of type MyClass, so one can add any object of type MyClass:

public void doSomething(List<? super MyClass> list) {
  MyClass m = new MyClass();
  list.add(m); // OK
}

However, it is not guaranteed that one can iterate over that list using a variable of type MyClass:

public void doSomething(List<? super MyClass> list) {
  for(MyClass object : list) { // Compile error
    // do something
  }
}

In order to be able to do both add objects of type MyClass to the list and iterate over it using a variable of type MyClass, a List<MyClass> is needed, which is the only type of List that is both List<? extends MyClass> and List<? super MyClass>.

The mnemonics PECS (Producer Extends, Consumer Super) from the book Effective Java by Joshua Bloch gives an easy way to remember when to use wildcards (corresponding to Covariance and Contravariance) in Java.

See also[edit]

References[edit]