Jump to content

Wildcard (Java)

From Alfawiki

In the Java programming language, the wildcard symbol ? serves as a distinctive type argument that governs type safety when working with generic types. It is applicable in variable declarations, object creation, and method signatures, but it is not employed in the declaration of generic types themselves. This mechanism exemplifies use-site variance annotation, contrasting with definition-site variance found in languages such as C# and Scala.

Covariance in generic types

In contrast to arrays—which are covariant in Java—different instantiations of a generic type are incompatible, even when casting explicitly. For instance, declaring Generic<Supertype> superGeneric; alongside Generic<Subtype> subGeneric; results in compilation errors if attempting to cast between these two.

This restriction can be mitigated by utilizing the wildcard ? as a type argument. The type Generic<?> functions as a common supertype for all variations of Generic, enabling assignments from both Generic<Supertype> and Generic<Subtype>. Specifying Generic<? extends Supertype> limits compatibility to Supertype and its subclasses, whereas Generic<? super Subtype> permits Subtype and its supertypes, thereby increasing the adaptability of APIs.

Wildcard usage as a parameter type

Within a generic class or method, the formal type parameter is interpreted as its upper bound (defined with extends; defaults to Object if no constraint is set). When a method returns this type parameter, the returned value (for example, of type ?) can be assigned to a variable typed as the upper bound or simply as Object. Nevertheless, the wildcard itself cannot accept any actual arguments—not even instances of Object. Values of the unknown type can be retrieved and assigned only to variables typed as a supertype of the upper bound.

Consider the following example for a class declared as Generic<T extends UpperBound>: <syntaxhighlight lang="java"> class Generic <T extends UpperBound> {

   private T t;
   void write(T t) {
       this.t = t;
   }
   T read() {
       return t;
   }

} </syntaxhighlight>

An example of usage: <syntaxhighlight lang="java"> final Generic<UpperBound> concreteTypeReference = new Generic<>(); final Generic<?> wildcardReference = concreteTypeReference; final UpperBound ub = wildcardReference.read(); // Object also allowed wildcardReference.write(new Object()); // compilation error wildcardReference.write(new UpperBound()); // compilation error concreteTypeReference.write(new UpperBound()); // valid </syntaxhighlight>

Bounded wildcards

A bounded wildcard imposes either an upper or lower inheritance constraint. This boundary may be defined by a class, interface, array, or type variable. The keyword extends indicates an upper bound, while super signifies a lower bound. Wildcards specify only one boundary—either upper or lower—but never both simultaneously.

Upper bounds

An upper bound must be a subtype of the specified type parameter's upper limit. For instance:

Generic<? extends SubtypeOfUpperBound> referenceConstrainedFromAbove;

This variable can reference any Generic instance whose type argument is a subtype of SubtypeOfUpperBound. If a wildcard does not explicitly declare an upper bound, it implicitly assumes extends Object, since all reference types in Java derive from Object.

Lower bounds

A wildcard with a lower bound, such as

Generic<? super SubtypeOfUpperBound> referenceConstrainedFromBelow;

can refer to any Generic parameterized with a type argument that is both a subtype of the original type parameter's upper bound and a supertype of SubtypeOfUpperBound.

Object creation with wildcard

Instantiating objects using a wildcard type argument is prohibited; for example, new Generic<?>() is invalid. This restriction exists because any concrete type satisfying the wildcard constraints can be used instead.

Conversely, expressions like new ArrayList<Generic<?>>() are valid since the wildcard applies to the generic parameter of ArrayList, not to the actual instantiated type. The same reasoning holds true for new ArrayList<List<?>>().

When creating arrays, the component type must be reifiable according to the Java Language Specification. If the component includes type parameters, they must be unbounded wildcards (i.e., ?). For example, new Generic<?>[20] is allowed, whereas new Generic<SomeType>[20] is disallowed.

Using raw types (omitting type parameters) is another possibility but leads to compiler warnings due to diminished type safety.

Example: Lists

Within the Java Collections Framework, List<MyClass> denotes an ordered collection containing elements of type MyClass.

Using upper bounds with extends: A List<? extends MyClass> can contain elements of some subclass of MyClass, enabling iteration with variables typed as MyClass: <syntaxhighlight lang="java"> public void doSomething(List<? extends MyClass> list) {

   for (final MyClass object : list) { // Valid
       // process object
   }

} </syntaxhighlight> However, adding new elements to such a list is prohibited: <syntaxhighlight lang="java"> public void doSomething(List<? extends MyClass> list) {

   final MyClass m = new MyClass();
   list.add(m); // Compilation error

} </syntaxhighlight>

Using lower bounds with super: A List<? super MyClass> accepts elements of any superclass of MyClass, permitting additions of MyClass instances: <syntaxhighlight lang="java"> public void doSomething(List<? super MyClass> list) {

   final MyClass m = new MyClass();
   list.add(m); // Allowed

} </syntaxhighlight> However, iterating over such a list with variables typed as MyClass is not guaranteed: <syntaxhighlight lang="java"> public void doSomething(List<? super MyClass> list) {

   for (final MyClass object : list) { // Compilation error
       // process object
   }

} </syntaxhighlight>

To both insert and iterate using MyClass, employing a plain List<MyClass> is necessary—this type meets both upper and lower bound requirements.

The acronym PECS (Producer Extends, Consumer Super), coined by Joshua Bloch in Effective Java, succinctly guides when to apply wildcards for greater API flexibility.

See also

References

  • Bloch, Joshua. Effective Java: Programming Language Guide. 3rd edition, Addison-Wesley, 2018. ISBN 978-0134685991
  • The Java Language Specification, Third Edition (Sun). ISBN 978-0-321-24678-3
  • Java Tutorials, Lesson on Generics
  • Capturing Wildcards
  • Typkompatibilität in Java (German language)