In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.
You can use an upper bounded wildcard to relax the restrictions on a variable. For example, say you want to write a method that works on List<Integer>
, List<Double>
, and List<Number>
; you can achieve this by using an upper bounded wildcard.
To declare an upper-bounded wildcard, use the wildcard character('?
'), followed by the extends
keyword, followed by its upper bound. Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).
To write the method that works on lists of Number
and the subtypes of Number
, you would specify List<? extends Number>
. The term List<Number>
is more restrictive than List<? extends Number>
because the former matches a list of type Number
only, whereas the latter matches a list of type Number
or any of its subclasses.
Consider the following process method:
public static void process(List<? extends Foo> list) { /* ... */ }
The upper bounded wildcard , <? extends Foo>
, where Foo
is any type, matches Foo
and any subtype of Foo
. The process method can access the listt elements as type Foo
:
public static void process(List<? extends Foo> list) {
for (Foo elem: list) {
// ...
}
}
In the foreach clause, the elem variable iterates over each element in the list. Any method defined in the Foo
class can now be used on elem.
The sumOfList
method returns the sum of the numbers in a list:
public static void double sumOfList(List<? extends Number> list) {
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;
}
The following code, using a list of Integer
objects, prints sum = 6.0:
List<Integer> li = Arrays.asList(1,2,3);
System.out.println("sum = " + sumofList(li));
A list of Double
values can use the same sumOfList
method. The following code prints sum = 7.0:
List<Double> ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));
The unbounded wildcard type is specified using the wildcard character (?
), for example, List<?>
. This is called a list of unknown type. There are two scenarios where an unbounded wildcard is a useful approach:
If you are writing a method that can be implemented using functionality provided in the Object
class.
When the code is using methods in the generic class that don't depend on the type parameter. For example, List.size
or List.clear
. In fact, Class<?>
is so often used because most of the methods in Class<T>
do not depend on T
.
Consider the following method, printList
:
public static void printList(List<Object> list) {
for (Object elem: list)
System.out.println(elem + " " ;
System.out.println();
}
The goal of printList
is to print a list of any type, but it fails to achieve that goal - it prints only a list of Object
instances; it cannot print List<Integer>
, List<String>
, List<Double>
, and so on, because they are not subtypes of List<Object>
. To write a generic printList
method, use List<?>
:
public static void printList(List<?> list) {
for (Object elem: list)
System.out.println(elem + " " ;
System.out.println();
}
Because for any concrete typeA
, List<A>
is a subtype of List<?>
, you can use printList
to print a list of any type:
List<Integer> li = Arrays.asList(1, 2, 3);
List<String> ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);
Note : The Arrays.asList method is used in examples throughout this lesson. This static factory method converts the specified array and returns a fixed-size list.
It's important to note that List<Object>
and List<?>
are not the same. You can insert an Object
, or any subtype of Object
, into a List<Object>
. But you can only insert null
into a List<?>
. The Guidelines for Wildcard Use section has more information on how to determine what kind of wildcard, if any, should be used in a given situation.
A lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.
A lower bounded wildcard is expressed using the wildcard character (?
), following by the super keyword, followed by its lower bound: <? super A>
.
Note: You can specify an upper bound for a wildcard, or you can specify a lower bound, but you cannot specify both.
Say you want to write a method that puts Integer
objects into a list. To maximize flexibility, you would like the method to work on List<Integer>
, List<Number>
, and List<Object>
- anything that can hold Integer
values.
To write the method that works on lists of Integer
and the supertypes of Integer
, such as Integer
, Number
, and Object
, you would specify List<? super Integer>
. The term List<Integer>
is more restrictive than List<? super Integer>
because the former matches a list of type Integer
only, whereas the latter matches a list of any type that is a supertype of Integer
.
The following code adds the numbers 1 throught 10 to the end of a list:
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10 ; i++) {
list.add(i);
}
}
The Guidelines for Wildcard Use section provides guidance on when to use upper bounded wildcards and when to use lower bounded wildcards.
As described in Generics, Inheritance, and Subtypes, generic classes or interfaces are not related merely because there is a relationship between their types. However, you can use wildcards to create a relationship between generic classes or interfaces.
Given the following two regular (non-generic) classes:
class A { /* ... */ }
class B extends A { /* ... */ }
It would be reasonalbe to write the following code:
B b = new B();
A a = b;
This example shows that inheritance of regular classes follows this rule of subtyping: class B is a subtype of class A if B extends A. This rule does not apply to generic types:
List<B> lb = new ArrayList<>();
List<A> la = lb; // compile-time error
Given that Integer
is a subtype of Number
, what is the relationship between List<Integer>
and List<Number>
?
Although Integer
is a subtype of Number
, List<Integer>
is not a subtype of List<Number>
and, in fact, these two types are not related. The common parent of List<number>
and List<Integer>
is List<?>
.
In order to create a relationship between these classes so that the code can access Number
's methods through List<inger>
's elements, use an upper bounded whildcard:
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>
Because Intger
is a subtype of Number
, and numList
is a list of Number
objects, a relationship now exists between intList
(a list of Integer
objects) and numList
. The following diagram shows the relationships between several List
classes declared with bouth upper and lower bounded wildcards.
The Guidelines for Wildcard Use section has more information about the ramifications of using upper and lower bounded wildcards.
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".
For more information, check this link.
One of the more confusing aspects when learning to program with generics is determining when to use an upper bounded wildcard and when to use a lower bounded wildcard. This page provides some guidelines to follow when designing your code.
For purposes of this discussion, it is helpful to think of variables as providing one of two functions:
An "In" Variable
copy(src, dest)
. The src
argument provides the data to be copied, so it is the "in" parameter.An "Out" Variable
copy(src, dest)
, the dest
argument accepts data, so it is the "out" parameter.You can use the "in" and "out" principle when deciding whether to use a wildcard and what type of wildcard is appropriate. The following list provides the guidelines to follow:
Wildcard Guidelines:
- An "in" variable is defined with an upper bounded wildcard, using the
extends
keyword.- An "out" variable is defined with a lower bounded wildcard, using the
super
keyword.- In the case where the "in" variable can be accessed using methods defined in the
Object
class, use an unbounded wildcard.- In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.
These guidelines do not apply to a method's return type. Using a wildcard as a return type should be avoided because it forces programmers using the code to deal with wildcards.
A list defined by List<? extends ...>
can be informally thought of as read-only, but that is not a strict guarantee. Suppose you have the following two classes:
class NaturalNumber {
private int i;
public NaturalNumber(int i) { this.i = i; }
// ...
}
class EvenNumber extends NaturalNumber {
public EvenNumber(int i) { super(i); }
// ...
}
Consider the following code:
List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35)); // compile-time error
Because List<EvenNumber>
is a subtype of List<? extends NaturalNumber>
, you can assign le
to ln
. But you cannot use ln
to add a natural number to a list of even numbers. The following operations on the list are possible:
you can see that the list defined by List<? extends NaturalNumber>
is not read-only in the strictest sense of the word, but you might think of it that way because you cannot store a new element or change an existing element in the list.