Wildcard (Java)


In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type. This is a form of use-site variance annotation, in contrast with the definition-site variance annotations found in C# and Scala.

Covariance for generic types

Unlike arrays, different instantiations of a generic type are not compatible with each other, not even explicitly. For example, the declarations

Generic superGeneric;
Generic subGeneric;

will cause the compiler to report conversion errors for both castings superGeneric and subGeneric.
This incompatibility can be softened by the wildcard if ? is used as an actual type parameter. Generic is a supertype of all parameterizarions of the generic type Generic. This allows objects of type Generic and Generic to be safely assigned to a variable or method parameter of type Generic. Using Genericextends Supertype> allows the same, restricting compatibility to Supertype and its children. Another possibility is Genericsuper Subtype>, which also accepts both objects and restricts compatibility to Subtype and all its parents.

Wildcard as parameter type

In the body of a generic unit, the type parameter is handled like its upper bound. If the return type of a method is the type parameter, the result can be referenced by a variable of the type of the upper bound. 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. However, objects of the unknown type can be read from the generic object and assigned to a variable of a supertype of the upperbound.
Sample code for the Genericextends UpperBound> class:

class Generic

Sample code that uses the Genericextends UpperBound> class:

...
final Generic concreteTypeReference = new Generic<>;
final Generic wildcardReference = concreteTypeReference;
final UpperBound ub = wildcardReference.get; // Object would also be OK
wildcardReference.set); // type error
wildcardReference.set); // type error
concreteTypeReference.set); // OK
...

Bounded wildcards

A bounded wildcard is one with either an upper or a lower inheritance constraint. The bound of a wildcard can be either a class type, interface type, array type, or type variable. Upper bounds are expressed using the extends keyword and lower bounds using the super keyword. Wildcards can have at most one bound - either an upper bound or a lower bound, but not both.

Upper bounds

An upper bound on a wildcard must be a subtype of the upper bound of the corresponding type parameter declared in the corresponding generic type. An example of a wildcard that explicitly states an upper bound is:
Genericextends SubtypeOfUpperBound> referenceConstrainedFromAbove;
This reference can hold any parameterization of Generic whose type argument is a subtype of SubtypeOfUpperBound. A wildcard that does not explicitly state an upper bound is effectively the same as one that has the constraint extends Object, since all reference types in Java are subtypes of Object.

Lower bounds

A wildcard with a lower bound, such as
Genericsuper SubtypeOfUpperBound> referenceConstrainedFromBelow;
can hold any parameterization of Generic whose any type argument is both a subtype of the corresponding type parameter's upper bound and a supertype of SubtypeOfUpperBound.

Object creation with wildcard

No objects may be created with a wildcard type argument: for example, new Generic is forbidden. 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 as the type argument.
However, new ArrayList> is allowed, because the wildcard is not a parameter to the instantiated type ArrayList. The same holds for new ArrayList>.
In an array creation expression, the component type of the array must be reifiable as defined by the Java Language Specification, Section 4.7. This entails that, if the component type of the array has any type arguments, they must all be unbounded wildcards. For example, new Generic is correct, while new Generic is not.
For both cases, using no parameters is another option. This will generate a warning since it is less type-safe.

Example: Lists

In the Java Collections Framework, the class List represents an ordered collection of objects of type MyClass.
Upper bounds are specified using extends:
A Listextends 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

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

public void doSomething

The converse is true for lower bounds, which are specified using super:
A Listsuper 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

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

public void doSomething

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 is needed, which is the only type of List that is both Listextends MyClass> and Listsuper MyClass>.
The mnemonics PECS from the book Effective Java by Joshua Bloch gives an easy way to remember when to use wildcards in Java.

Type constraints in other languages

In C++, generic type constraints can be expressed using concepts.

import std;
using std::is_base_of_v;
using std::vector;
class Player ;
template
concept ExtendsPlayer = is_base_of_v;
// T is required to be a type whose inheritance upper bound is Player,
// blocking any type that does not inherit from Player
template
void processListOfPlayers

This example is equivalent to the following Java code:

import java.util.List;
class Player
public class Example

In C#, a generic type constraint is expressed with a where clause. These are more expressive and powerful than Java wildcards.

using System;
public class MyGenericClass
where T : IComparable, allows ref struct
where U : class, notnull, new

Kotlin, though also a JVM language, does not support Java-style type wildcards. However, it represents ? instead represented as *. It otherwise has C#-style where clauses:

fun copyWhenGreater: List
where T : CharSequence, T : Comparable

Rust also uses where clauses to bound traits.

use std::cmp::Ord;
struct MyStruct where T: Ord + Default
impl MyStruct where T: Ord + Default