Java Generics FAQs - Type Arguments
Type
Arguments
© Copyright 2004-2022 by Angelika Langer. All
Rights Reserved.
Type Arguments
Fundamentals
What
is a type argument?
Which
types are permitted as type arguments?
All references types including parameterized
types, but no primitive types.
|
All reference types can be used a type arguments of a parameterized
type or method. This includes classes, interfaces, enum types, nested
and inner types, and array types. Only primitive types cannot be
used as type argument.
Example (of types as type arguments of a parameterized type):
List<
int
>
l0;
//
error
List<
String
>
l1;
List<
Runnable
>
l2;
List<
TimeUnit
>
l3;
List<
Comparable
>
l4;
List<
Thread.State
>
l5;
List<
int[]
>
l6;
List<
Object[]
>
l7;
List<
Callable<String>
>
l8;
List<
Comparable<? super Long>
>
l9;
List<
Class<? extends Number>
>
l10;
List<
Map.Entry<?,?>
>
l11;
The code sample shows that a primitive type such as
int
is not
permitted as type argument.
Class types, such as
String
, and interface types, such as
Runnable
,
are permitted as type arguments. Enum types, such as
TimeUnit
(see
java.util.concurrent.TimeUnit
),
are also permitted as type arguments.
Raw types are permitted as type arguments;
Comparable
is an example.
Thread.State
is an example of a nested type;
Thread.State
is
an enum type nested into the
Thread
class. Non-static inner
types are also permitted.
An array type, such as
int[]
and
Object[]
, is permitted
as type arguments of a parameterized type or method.
Parameterized types are permitted as type arguments, including concrete
parameterized types such as
Callable<String>
, bounded
wildcard parameterized types such as
Comparable<? super Long>
and
Class<?
extends Number>
, and unbounded wildcard parameterized types such as
Map.Entry<?,?>
.
The same types are permitted as explicit type arguments of a generic
method.
Example (of types as type arguments of a generic method):
List<?> list;
list = Collections.<
int
>emptyList();
// error
list = Collections.<
String
>emptyList();
list = Collections.<
Runnable
>emptyList();
list = Collections.<
TimeUnit
>emptyList();
list = Collections.<
Comparable
>emptyList();
list = Collections.<
Thread.State
>emptyList();
list = Collections.<
int[]
>emptyList();
list = Collections.<
Object[]
>emptyList();
list = Collections.<
Callable<String>
>emptyList();
list = Collections.<
Comparable<?
super Long>
>emptyList();
list = Collections.<
Class<? extends
Number>
>emptyList();
list = Collections.<
Map.Entry<?,?>
>emptyList();
The code sample shows that primitive types such as
int
are not
permitted as type argument of a generic method either. |
LINK TO THIS
|
TypeArguments.FAQ002
|
REFERENCES
|
What
is a type argument?
|
Are
primitive types permitted as type arguments?
No. Only reference types can be used
as type arguments.
|
A parameterized type such as
List<int>
or
Set<short>
is illegal. Only reference types can be used for instantiation of
generic types and methods. Instead of
List<int>
we must declare
a
List<Integer
>, using the corresponding wrapper type as the
type argument.
The lack of primitive type instantiations is not a major restriction
in practice (except for performance reasons), because autoboxing and auto-unboxing
hides most of the nuisance of wrapping and unwrapping primitive values
into their corresponding wrapper types.
Example (of autoboxing):
int[] array = {1,2,3,4,5,6,7,8,9,10};
List<Integer> list = new LinkedList<Integer>();
for (int i : array)
list.add(i);
//
autoboxing
for (int i=0;i<list.size();i++)
array[i] = list.get(i);
//
auto-unboxing
Here we insert primitive type
int
values to the list of
Integer
s,
relying on autoboxing, which is the automatic conversion from the primitve
type to the corresponding wrapper type. Similarly, we extract primitive
type
int
values from the list, relying on auto-unboxing,
which is the automatic conversion from the wrapper type to the corresponding
primitive type.
Note, that the lack of primitive type instantiations incurs a performance
penalty. Autoboxing and -unboxing make the use of wrapper type instantiations
of generic types very convenient and concise in the source code.
But the concise notation hides the fact that behind the curtain the virtual
machine creates and uses lots of wrapper objects, each of which must be
allocated and later garbage collected. The higher performance of direct
use of primitive type values cannot be achieved with generic types.
Only a regular (i.e., non-generic) type can provide the optimal performance
of using primitive type values.
Example:
class Box
<T>
{
private
T
theObject;
public Box(
T
arg) {
theObject = arg; }
public
T
get() { return
theObject; }
...
}
class BoxOfLong {
private
long
theObject;
public BoxOfLong(
long
arg) { theObject = arg; }
public
long
get() {
return theObject; }
...
}
class Test {
public static void main(String[] args) {
long result;
Box
<Long>
box = new Box
<Long>
(0L);
//
autoboxing
result = box.get();
// auto-unboxing
Box
<long>
box = new Box
<long>
(0L);
//
error
result = box.get();
BoxOfLong box = new BoxOfLong(0L);
result = box.get();
}
}
The example illustrates that the instantiation of the
Box
type
for type
Long
leads to the inevitable overhead of boxing and unboxing.
The instantiation on the primitive type
long
does not help, because
it is illegal. Only a dedicated non-generic
BoxOfLong
type
eliminates the overhead by using the primitive type
long
. |
LINK TO THIS
|
TypeArguments.FAQ003
|
REFERENCES
|
What
is a type argument?
Which
types are permitted as type arguments?
|
Are
wildcards permitted as type arguments?
For instantiation of a generic type,
yes. For instantiation of a generic method, no.
|
A wildcard is a syntactic construct that denotes a family
of types.
All wildcards can be used as type arguments of a parameterized type.
This includes the unbounded wildcard as well as wildcards with an upper
or lower bound.
Examples:
List<
?
>
l0;
List<
? extends Number
>
l1;
List<
? super Long
>
l2;
Wildcards can
not
be used as type arguments of a generic method.
Examples:
list = Collections.<
?
>emptyList();
//error
list = Collections.<
? extends Number
>emptyList();
//error
list = Collections.<
? super Long
>emptyList();
//error
|
LINK TO THIS
|
TypeArguments.FAQ004
|
REFERENCES
|
What
is a wildcard?
What
is an unbounded wildcard?
What
is a bounded wildcard?
|
Are
type parameters permitted as type arguments?
Yes.
|
Type parameters of a generic type or method can be used
as arguments of parameterized types or methods.
Example (of instantiations of a generic type using a type parameter
as type argument):
class someClass
<T>
{
public List<T> someMethod() {
List<
T
>
list = Collections.<
T
>emptyList();
...
return list;
}
public static
<S>
void anotherMethod(S arg) {
List<
S
>
list = Collections.<
S
>emptyList();
...
}
}
The example above demonstrates how the type parameter
T
of the
enclosing generic class and the type parameter
S
of a generic
method can be used as type arguments to both a parameterized type, namely
List
,
and a generic method, namely
emptyList
. |
LINK TO THIS
|
TypeArguments.FAQ005
|
REFERENCES
|
What
is a type argument?
What
is a type parameter?
What
is a parameterized (or generic) method?
What
is a parameterized (or generic) type?
|
Do
type parameter bounds restrict the set of types that can be used as type
arguments?
Yes, type arguments must be within bounds.
|
When a formal type parameter is declared with one or several
bounds, then the actual type argument must be a subtype of all of the bounds
specified for the respective formal type parameter.
Examples:
class Wrapper<T extends Comparable
<T>
>
implements Comparable
<Wrapper<T>>
{
private final T theObject;
public Wrapper(T t) { theObject = t; }
public T getWrapper() { return theObject; }
public int compareTo(Wrapper
<T>
other) { return theObject.compareTo(other.theObject); }
}
Wrapper
<String>
wrapper1 = new Wrapper
<String>
("Oystein");
Wrapper
<? extends Number>
wrapper2 = new Wrapper
<Long>
(0L);
Wrapper
<?>
wrapper3 = new Wrapper
<Date>
(new Date());
Wrapper
<Number>
wrapper4 = new Wrapper
<Number>
(new Long(0L));
// error
Wrapper
<int>
wrapper5 = new Wrapper
<int>
(5); //
error
Comparable<T>
uses a type parameter as its type argument.
Comparable<Wrapper<T>>
uses an instantiation of a parameterized
type as type argument.
Wrapper<String>
and
Wrapper<Long>
have concrete
reference types as type arguments.
Wrapper<? extends Number>
and
Wrapper<?>
use
wildcards as type arguments.
Wrapper<Number>
is illegal because
Number
is not
a subtype of
Comparable<Number>
and is not within bounds.
Wrapper<int>
is illegal because primitive types are not
allowed as type arguments. |
LINK TO THIS
|
TypeArguments.FAQ006
|
REFERENCES
|
What
is a type parameter?
What
is a bounded type parameter?
|
Do
I have to specify a type argument when I want to use a generic type?
Do
I have to specify a type argument when I want to invoke a generic method?
No; a generic method can be used without
type arguments.
|
A generic method can be invoked like a regular method,
that is, without specification of the type arguments. In such a case
the compiler will automatically infer the type arguments from the static
types of the method arguments or the context of the method invocation.
This process is known as
type argument inference
. |
LINK TO THIS
|
TypeArguments.FAQ008
|
REFERENCES
|
What
is type argument inference?
What
is explicit type argument specification?
|
Wildcards
What
is a wildcard?
What
is an unbounded wildcard?
What
is a bounded wildcard?
A wildcard with either an upper or a
lower bound.
|
A wildcard with an upper bound looks like "
? extends
Type
" and stands for the family of all types that are subtypes of
Type
,
type
Type
being included.
Type
is called the
upper
bound
.
A wildcard with a lower bound looks like "
? super Type
" and
stands for the family of all types that are supertypes of
Type
,
type
Type
being included.
Type
is called the
lower
bound
.
Bounded wildcards are used as arguments for instantiation of generic
types. Bounded wildcards are useful in situations where only partial
knowledge about the type argument of a parameterized type is needed, but
where unbounded wildcards carry too little type information.
Example:
public class Collections {
public static
<T> void
copy
(
List<? super T>
dest,
List<? extends T>
src) {
// bounded wildcard parameterized types
for (int i=0; i<src.size(); i++)
dest.set(i,src.get(i));
}
}
The
copy
method copies elements from a source list into a destination
list. The destination list must be capable of holding the elements
from the source list. We express this by means of bounded wildcards:
the output list is required to have an element type with a lower bound
T
and the input list must have an element type with an upper bound
T
.
Let's study an example to explore the typical use of bounded wildcards
and to explain why unbounded wildcards do not suffice. It's the example
of the
copy
method mentioned above. It copies elements from
a source list into a destination list. Let's start with a naive implementation
of such a
copy
method.
Example (of a restrictive implementation of a
copy
method):
public class Collections {
public static
<T>
void copy(
List<T>
dest,
List<T>
src) { // uses no wildcards
for (int i=0; i<src.size(); i++)
dest.set(i,src.get(i));
}
}
This implementation of a
copy
method is more restrictive than
it need be, because it requires that both input and output collection must
be lists with the exact same type. For instance, the following invocation
- although perfectly sensible - would lead to an error message:
Example (of illegal use of the
copy
method):
List<Object> output = new ArrayList<
Object
>();
List<Long> input = new
ArrayList<
Long
>();
...
Collections.copy(output,input);
//
error: illegal argument types
The invocation of the
copy
method is rejected because the declaration
of the method demands that both lists must be of the same type. Since
the source list is of type
List<Long>
and the destination list
is of type
List<Object>
the compiler rejects the method call,
regardless of the fact that a list of
Object
references can hold
Long
s.
If
both
list were of type
List<Object>
or both were
of type
List<Long>
the method call were accepted.
We could try to relax the method's requirements to the argument types
and declare wildcard parameterized types as the method parameter types.
Declaring wildcard parameterized types as method parameter types has the
advantage of allowing a broader set of argument types. Unbounded
wildcards allow the broadest conceivable argument set, because the unbounded
wildcard
?
stands for any type without any restrictions.
Let's try using an unbounded wildcard parameterized type. The method
would then look as follows:
Example (of a relaxed
copy
method; does not compile):
public class Collections {
public static void copy(
List<?>
dest,
List<?>
src) { // uses
unbounded wildcards
for (int i=0; i<src.size(); i++)
dest.set(i,src.get(i));
//
error: illegal argument types
}
}
It turns out that this relaxed method signature does not compile.
The problem is that the
get()
method of a
List<?>
returns a reference pointing to an object of unknown type. References
pointing to objects of unknown type are usually expressed as a reference
of type
Object
. Hence
List<?>.get()
returns
an
Object
.
On the other hand, the
set()
method of a
List<?>
requires something unknown, and "unknown" does not mean that the required
argument is of type
Object
. Requiring an argument of type
Object
would mean accepting everything that is derived of
Object
.
That's not what the
set()
method of a
List<?>
is asking
for. Instead, "unknown" in this context means that the argument must be
of a type that matches the type that the wildcard
?
stands for.
That's a much stronger requirement than just asking for an
Object
.
For this reason the compiler issues an error message:
get()
returns an
Object
and
set()
asks for a more specific,
yet unknown type. In other words, the method signature is too relaxed.
Basically, a signature such as
void copy(
List<?>
dest,
List<?>
src)
is saying
that the method takes one type of list as a source and copies the content
into another - totally unrelated - type of destination list. Conceptually
it would allow things like copying a list of apples into a list of oranges.
That's clearly not what we want.
What we really want is a signature that allows copying elements from
a source list into a destination list with a specific property, namely
that it is capable of holding the source list's elements. Unbounded wildcards
are too relaxed for this purpose, as we've seen above, but bounded wildcards
are suitable in this situation. A bounded wildcard carries more information
than an unbounded wildcard.
In our example of a
copy
method we can achieve our goal of
allowing all sensible method invocations by means of bounded wildcards,
as in the following implementation of the
copy
method:
Example (of an implementation of the
copy
method that uses
bounded wildcards):
public class Collections {
public static
<T>
void copy
(
List<? super T>
dest,
List<? extends T>
src) {
// uses bounded wildcards
for (int i=0; i<src.size(); i++)
dest.set(i,src.get(i));
}
}
In this implementation we require that a type
T
exists that is
subtype of the output list's element type and supertype of the input list's
element type. We express this by means of wildcards: the output list
is required to have an element type with a lower bound
T
and the
input list must have an element type with an upper bound
T
.
Example (of using the
copy
method with wildcards):
List<Object> output = new ArrayList<
Object
>();
List<Long> input = new ArrayList<
Long
>();
...
Collections.copy(output,input);
//
fine; T:= Number & Serializabe & Comparable<Number>
List<String> output = new ArrayList<
String
>();
List<Long> input = new ArrayList<
Long
>();
...
Collections.copy(output,input);
// error
In the first method call
T
would have to be a supertype of
Long
and a subtype of
Object
, and luckily there is a number of types
that fall into this category, namely
Number
,
Serializable
and
Comparable<Number>
. Hence the compiler can use any
of the 3 types as type argument and the method invocation is permitted.
The second nonsensical method call is rejected by the compiler, because
the compiler realizes that there is no type that is subtype of
String
and supertype of
Long
.
Conclusion
:
Bounded wildcards carry more information than unbounded wildcards.
While an unbounded wildcard stands for a representative from the family
of
all
types, a bounded wildcards stands for a represenstative of
a family of either super- or subtypes of a type. Hence a bounded
wildcard carries more type information than an unbounded wildcard.
The supertype of such a family is called the upper bound, the subtype of
such a family is called the lower bound. |
LINK TO THIS
|
TypeArguments.FAQ103
|
REFERENCES
|
What
is a wildcard?
What
is a wildcard instantiation?
How
do wildcard instantiations with an upper bound relate to other instantiations
of the same generic type?
How
do wildcard instantiations with a lower bound relate to other instantiations
of the same generic type?
Which
super-subset relationships exist among wildcards?
What
is a wildcard bound?
Which
types are permitted as wildcard bounds?
What
is the difference between a wildcard bound and a type parameter bound?
What
is the difference between a Collection<?> and a Collection<Object>?
|
What
do multi-level (i.e., nested) wildcards mean?
It depends.
|
A multi-level wildcard is a wildcard that appears as the
type argument of a type argument. The instantiations
Collection<Pair<String,?>>
and
Collection<? extends Pair<String,?>>
are examples of
multi-level wildcards. Multi-level wildcard parameterized types are inherently
difficult to interpret, because the wildcard can appear on different levels.
For illustration, let us discuss the difference between a
Collection<Pair<String,?>>
and a
Collection<? extends Pair<String,?>>
. For sake
of simplicity, let us assume
Pair
is a
final
class.
The type
Collection<Pair<String,?>>
is a concrete instantiation
of the generic
Collection
interface. It is a heterogenous
collection of pairs of different types. It can contain elements of
type
Pair<String,Long>
,
Pair<String,Date>
,
Pair<String,Object>
,
Pair<String,String>
,
and so on and so forth. In other words,
Collection<Pair<String,?>>
contains a mix of pairs of different types of the form
Pair<String,?>
.
The type
Collection<? extends Pair<String,?>>
is
a wildcard parameterized type; it does NOT stand for a concrete parameterized
type. It stands for a representative from the family of collections
that are instantiations of the
Collection
interface, where the
type argument is of the form
Pair<String,?>
. Compatible
instantiations are
Collection<Pair<String,Long>>
,
Collection<Pair<String,String>>
,
Collection<Pair<String,Object>>
,
or
Collection<Pair<String,?>>
. In other words, we do not
know which instantiation of
Collection
it stands for.
As a rule of thumb, you have to read multi-level wildcards top-down.
If the top-level type argument is a concrete type then the instantiation
is a concrete type, probably a mixed bag of something, if the wildcard
appears further down on a lower level. In this sense, a
Collection<Pair<String,?>>
is
a collection of pairs of a certain form, that has lots of concrete subtypes.
It's similar to a
Collection<Object>
, which is a collection
of a concrete type that has lots of subtypes and is a mixed bag of something
that is a subtype of
Object
.
If the top-level type argument is a wildcard, then the type is not concrete.
It is a placeholder for a representative from a family of types. In this
sense, a
Collection<? extends Pair<String,?>>
is a placeholder
for a collection instantiated for a particular unknown pair type of a certain
form. It's similar to a
Collection<?>
, which is a placeholder
for a specific instantiation of the
Collection
interface, but
it is not a concrete type.
Here is an example that illustrates the difference between
Collection<Pair<String,?>>
and
Collection<?
extends Pair<String,?>>
.
Example:
Collection<
Pair<String,Long>
>
c1 = new ArrayList<Pair<String,Long>>();
Collection<
Pair<String,Long>
>
c2 = c1;
// fine
Collection<
Pair<String,?>
>
c3 = c1;
// error
Collection<
? extends Pair<String,?>
> c4 = c1;
//
fine
Of course, we can assign a
Collection<Pair<String,Long>>
to a
Collection<Pair<String,Long>>
. There is nothing
surprising here.
But we can
not
assign a
Collection<Pair<String,Long>>
to a
Collection<Pair<String,?>>
. The parameterized
type
Collection<Pair<String,Long>>
is a homogenous collection
of pairs of a
String
and a
Long
; the parameterized
type
Collection<Pair<String,?>>
is a heterogenous collection
of pairs of a
String
and something of unknown type. The
heterogenous
Collection<Pair<String,?>>
could for instance
contain a
Pair<String,Date>
and that clearly does not belong
into a
Collection<Pair<String,Long>>
. For this reason
the assignment is not permitted.
But then, we can assign a
Collection<Pair<String,Long>>
to a
Collection<? extends Pair<String,?>>
, because the type
Pair<String,Long>
belongs
to the family of types denoted by the wildcard
? extends Pair<String,?>
.
This is because the type family denoted by the wildcard
? extends Pair<String,?>
comprises all subtypes of
Pair<String,?>
. Since we assumed
that
Pair
is a
final
type, this type family includes
all instantiations of the generic type
Pair
where the first type
argument is
String
and second type argument is an arbitrary type
or wildcard. The type family includes members such as
Pair<String,Long>
,
Pair<String,Object>
,
Pair<String,?
extends Number>
, and
Pair<String,?>
itself.
If we give up the simplification that
Pair
is a
final
class, then we must also consider subtypes of
Pair
.
Collection<Pair<String,?>>
is then a heterogenous collection
of pairs of different types of the form
Pair<String,?>
,
or
subtypes thereof
. It can contain elements of type
Pair<String,Long>
,
Pair<String,Date>
,
but also elements of type
SubTypeOfPair<String,Date>
,
SubTypeOfPair<String,Object>
,
and so on and so forth.
Collection<? extends Pair<String,?>>
stands for a representative
from the family of collections that are instantiations of the
Collection
interface, where the type argument is of the form
Pair<String,?>
,
or
subtypes thereof
. Compatible parameterized types are
Collection<Pair<String,Long>>
,
Collection<Pair<String,Object>>
,
but also
Collection<SubTypeOfPair<String,Object>>
, or
Collection<SubTypeOfPair<String,?>>
.
Here is an example that illustrates the difference between the concrete
parameterized type
Collection<Pair<String,?>>
and the wildcard
parameterized type
Collection<? extends Pair<String,?>>
.
Example:
Collection<
SubTypeOfPair<String,Long>
> c1
= new ArrayList<SubTypeOfPair<String,Long>>();
Collection<
Pair<String,Long>
>
c2 = c1;
// error
Collection<
SubTypeOfPair<String,Long>
> c3 = c1;
//
fine
Collection<
Pair<String,?>
>
c4 = c1;
// error
Collection<
? extends Pair<String,?>
> c5
= c1;
// fine
In this case, we can
not
assign a
Collection<SubTypeOfPair<String,Long>>
to a
Collection<Pair<String,Long>>
, because these two instantiations
of the generic type Collection are unrelated and incompatible types.
The
Collection<SubTypeOfPair<String,Long>>
contains
SubTypeOfPair<String,Long>
objects,
whereas the
Collection<Pair<String,Long>>
contains a mix
of objects of types that are subtypes of type
Pair<String,Long>
.
This includes, but is not limited to objects of type
SubTypeOfPair<String,Long>
.
For this reason a
Collection<SubTypeOfPair<String,Long>>
cannot be assigned to a
Collection<Pair<String,Long>>
.
Also, we can
not
assign a
Collection<SubTypeOfPair<String,Long>>
to a
Collection<Pair<String,?>>
because the parameterized
type
Collection<SubTypeOfPair<String,Long>>
is a homogenous
collection of objects of type
SubTypeOfPair<String,Long>
,
whereas the parameterized type
Collection<Pair<String,?>>
is
a heterogenous collection. The heterogenous
Collection<Pair<String,?>>
could
for instance contain a
Pair<String,Date>
and that clearly does
not belong in a
Collection<SubTypeOfPair<String,Long>>
.
The assignment of a
Collection<SubTypeOfPair<String,Long>>
to a
Collection<? extends Pair<String,?>>
is fine because
the type
SubTypeOfPair<String,Long>
belongs to the family of
types denoted by the wildcard
? extends Pair<String,?>
.
This is because the type family denoted by the wildcard
? extends Pair<String,?>
comprises all subtypes of
Pair<String,?>
. Since we no
longer assumed that
Pair
is a
final
type, this type family
includes all instantiations of the generic type
Pair
and any of
its subtypes where the first type argument is
String
and second
type argument is an arbitrary type or wildcard. The type family includes
members such as
Pair<String,Long>
,
Pair<String,Object>
,
Pair<String,?
extends Number>
, and
Pair<String,?>
itself, but also
SubTypeOfPair<String,Long>
,
SubTypeOfPair<String,Object>
,
SubTypeOfPair<String,?
extends Number>
, and
SubTypeOfPair<String,?>
. |
LINK TO THIS
|
TypeArguments.FAQ104
|
REFERENCES
|
What
is the difference between a Collection<?> and a Collection<Object>?
Which
super-subset relationships exist among wildcards?
What
is the difference between a Collection<Pair<String,Object>>, a Collection<Pair<String,?>>
and a Collection<? extends Pair<String,?>>?
|
If
a wildcard appears repeatedly in a type argument section, does it stand
for the same type?
No, each occurrence can stand for a different
type.
|
If the same wildcard appears repeatedly in a type argument
section each occurrence of the wildcard refers to a potentially different
type. It is similar to wildcards in a regular expression: in "s??"
the wildcard need not stand for the same character. "see", but also
"sea" or "sew" or "saw" would be matching expressions. Each question mark
stands for a potentially different character. The same holds for wildcards
in Java generics.
Example (using the same wildcard repeatedly):
Pair<
?
,
?
> couple = new Pair<
String
,
String
>("Orpheus","Eurydike");
Pair<
?
,
?
> xmas
= new Pair<
String
,
Date
>("Xmas",
new Date(104,11,24));
In the example above, the wildcard "
?
" can stand for the same
type, such as
String
, but it need not do so. Each wildcard
can stand for a different type, such as
String
and
Date
for instance.
Conversely, different wildcards need not stand for different types.
If the type families denoted by the two different wildcards overlap, then
the two different wildcards can stand for the same concrete type.
Example (using different wildcards):
Pair<
? extends Appendable
,
?
extends CharSequence
> textPlusSuffix
= new Pair<
StringBuilder
,
String
>(new
StringBuilder("log"), ".txt");
Pair<
? extends Appendable
,
? extends
CharSequence
> textPlusText
= new Pair<
StringBuilder
,
StringBuilder
>(new
StringBuilder("log"), new StringBuilder(".txt"));
In the examples above, the different wildcards "
? extends Appendable
"
and "
? extends CharSequence
" can stand for different types, such
as
StringBuilder
and
String
, but they can equally well
stand for the same type, such as
StringBulder
, provided the bounds
permit it.
Below are a couple of examples where the same wildcard appears repeatedly
and where the compiler rightly issues an error message.
In the first example the wildcard appears twice in an parameterized
type, namely in
Pair<?,?>
.
Example #1 (demonstrating the incompatibility of one wildcard to another):
class Pair<S,T> {
private S first;
private T second;
public Pair(S s,T t) { first = s; second = t; }
...
public static void flip(
Pair<
?
,
?
>
pair) {
Object tmp = pair.first;
pair.first = pair.second;
//
error: incompatible types
pair.second = tmp;
//
error: incompatible types
}
}
class Test {
public static void test() {
Pair<?,?> xmas = new Pair<
String
,
Date
>("Xmas",new
Date(104,11,24));
Pair.flip(xmas);
Pair<?,?> name = new Pair<
String
,
String
>("Yves","Meyer");
Pair.flip(name);
}
}
The fields of the
Pair<?,?>
may be of different types. For
instance, when the
flip
method is invoked with an argument of
type
Pair<String,Date>
, then
first
would be of type
String
,
while
second
is of type
Date
, and the fields cannot
be assigned to each other. Even if the
flip
method is invoked
with an argument of type
Pair<String,String>
, i.e. both wildcards
stand for the same type, namely
String
, the compiler does not
know this inside the implementation of the
flip
method.
For this reason the compiler issues an error message in the implementation
of the
flip
method when the two fields of unknown - and potentially
different types - are assigned to each other.
In the second example the wildcard appears in an two instantiations
of the same generic type, namely in
Collection<?>
.
Example #2 (demonstrating the incompatibility of one wildcard to another):
class Utilities {
public static void add(
Collection<
?
>
list1,
Collection<
?
>
list2) {
for (Object o : list1)
list2.add(o);
// error: incompatible types
}
}
class Test {
public static void test() {
Collection<?> dates = new LinkedList<
Date
>();
Collection<?> strings = new LinkedList<
String
>();
add(dates,strings);
add(strings,strings);
}
}
The two collections contain elements of two potentially different unknown
types. The compiler complains when elements from one collection are
passed to the other collection, because the types of the elements might
be incompatible.
In the third example the wildcard appears in an two instantiations of
two different generic type, namely in
Collection<? extends CharSequence>
and
Class<?
extends CharSequence>
.
Example #3 (demonstrating the incompatibility of one wildcard to another):
class Utilities {
public static void method(
Collection<
?
extends CharSequence
>
coll,
Class <
? extends CharSequence
>
type) {
...
coll.add(type.newInstance());
//
error: incompatible types
...
}
}
class Test {
public static void test() {
List<
StringBuilder
>
stringList = new LinkedList<StringBuilder>();
method(stringList,
String
.class);
}
}
The
newInstance
method of class
Class<? extends CharSequence>
creates an object of an unknown subtype of
CharSequence
, while
the collection
Collection<? extends CharSequence>
holds elements
of a potentially different subtype of
CharSequence
. The compiler
complains when the newly created object is passed to the collection, because
the collection might hold objects of an incompatible type. |
LINK TO THIS
|
TypeArguments.FAQ105
|
REFERENCES
|
What
is a wildcard instantiation?
How
can I make sure that the same wildcard stand for the same type?
What
is a wildcard bound?
What
is an unbounded wildcard?
What
is a bounded wildcard?
Which
super-subset relationships exist among wildcards?
|
Wildcard Bounds
What
is a wildcard bound?
A reference type that is used to further
describe the family of types denoted by a wildcard.
|
A wildcard can be unbounded, in which case it is denoted
as "
?
". The unbounded wildcard stands for the family of
all
types.
Alternatively a wildcard can have a bound. There are two types
of bounds: upper and lower bounds. The syntax for a bounded wildcard
is either "
? extends
SuperType
" (wildcard with upper bound)
or "
? super
SubType
" (wildcard with lower bound).
The terms "upper" and "lower" stem from the way, in which inheritance relationships
between types are denoted in modeling languages such as UML for instance.
The bound shrinks the family of types that the wildcard stands for. For
instance, the wildcard "
? extends Number
" stands for the family
of subtypes of
Number
; type
Number
itself is included
in the family. The wildcard "
? super Long
" stands
for the family of supertypes of
Long
; type
Long
itself is included.
Note, a wildcard can have only one bound. In can neither have both an
upper
and
a lower bound nor several upper or lower bounds.
Constructs such as "
? super Long extends Number
" or
"
? extends Comparable<String> & Cloneable
" are illegal. |
LINK TO THIS
|
TypeArguments.FAQ201
|
REFERENCES
|
What
is a wildcard?
Which
types are permitted as wildcard bounds?
What
is the difference between a wildcard bound and a type parameter bound?
|
Which
types are permitted as wildcard bounds?
All references types including parameterized
types, but no primitive types.
|
All reference types can be used as a wildcard bound.
This includes classes, interfaces, enum types, nested and inner types,
and array types. Only primitive types cannot be used as wildcard
bound.
Example (of wildcard bounds):
List<? extends
int
>
l0;
//
error
List<? extends
String
>
l1;
List<? extends
Runnable
>
l2;
List<? extends
TimeUnit
>
l3;
List<? extends
Comparable
>
l4;
List<? extends
Thread.State
>
l5;
List<? extends
int[]
>
l6;
List<? extends
Object[]
>
l7;
List<? extends
Callable<String>
>
l8;
List<? extends
Comparable<? super
Long>
>
l9;
List<? extends
Class<? extends Number>
>
l10;
List<? extends
Map.Entry<?,?>
>
l11;
List<? extends
Enum<?>
>
l12;
The example only shows the various reference types as upper bound of a
wildcard, but these type are permitted as lower bound as well.
We can see that primitive types such as
int
are not permitted
as wildcard bound.
Class types, such as
String
, and interface types, such as
Runnable
,
are permitted as wildcard bound. Enum types, such as
TimeUnit
(see
java.util.concurrent.TimeUnit
)
are also permitted as wildcard bound. Note, that even types that
do not have subtypes, such as final classes and enum types, can be used
as upper bound. The resulting family of types has exactly one member
then. For instance, "
? extends
String
"
stands for the type family consisting of the type
String
alone.
Following the same line of logic, the wildcard "
? super Object
"
is permitted, too, although class
Object
does not have a supertype.
The resulting type family consists of type
Object
alone.
Raw types are permitted as wildcard bound;
Comparable
is an example.
Thread.State
is an example of a nested type;
Thread.State
is
an enum type nested into the
Thread
class. Non-static inner
types are also permitted.
An array type, such as
int[]
and
Object[]
, is permitted
as wildcard bound. Wildcards with an array type as a bound denote the family
of all sub- or supertypes of the wildcard type. For instance, "
?
extends Object[]
" is the family of all array types whose component
type is a reference type.
int[]
does not belong to that
family, but
Integer[]
does. Similarly, "
? super Number[]
"
is the family of all supertypes of the array type, such as
Object[]
,
but also
Object
,
Cloneable
and
Serializable
.
Parameterized types are permitted as wildcard bound, including concrete
parameterized types such as
Callable<String>
, bounded
wildcard parameterized types such as
Comparable<? super Long>
and
Class<?
extends Number>
, and unbounded wildcard parameterized types such as
Map.Entry<?,?>
.
Even the primordial supertype of all enum types, namely class
Enum
,
can be used as wildcard bound.
|
LINK TO THIS
|
TypeArguments.FAQ202
|
REFERENCES
|
What
is a wildcard?
What
is a wildcard bound?
|
What
is the difference between a wildcard bound and a type parameter bound?
A wildcard can have only one bound, while
a type parameter can have several bounds.
A wildcard can have a lower or an upper bound, while there is
no such thing as a lower bound for a type parameter.
|
Wildcard bounds and type parameter bounds are often confused,
because they are both called bounds and have in part similar syntax.
Example (of type parameter bound and wildcard bound):
class Box<
T
extends
Appendable & Flushable
> {
private T theObject;
public Box(T arg)
{ theObject = arg; }
public Box(Box<
?
extends
T
> box) { theObject = box.theObject; }
...
}
The code sample above shows a type parameter
T
with two bounds,
namely
Appendable
and
Flushable
, and a wildcard with
an upper bound
T
.
The type parameter bounds give access to their non-static methods.
For instance, in the example above, the bound
Flushable
makes
is possible that the
flush
method can be invoked on variables
of type
T
. In other words, the compiler would accept an
expression such as
theObject.flush().
The wildcard bound describes the family of types that the wildcard stands
for. In the example, the wildcard "
? extends T
" denotes the family
of all subtypes of
T
. It is used in the argument type of
a constructor and permits that box objects of a box type from the family
Box<?
extends T>
can be supplied as constructor arguments. It allows
that a
Box<Writer>
can be constructed from a
Box<PrintWriter>
,
for instance.
The syntax is similar and yet different:
|
Syntax
|
type parameter bound
|
TypeParameter extends Class & Interface
1
& ...
& Interface
N
|
wildcard bound
|
|
upper bound
lower bound
|
? extends SuperType
? super SubType
|
|
|
A wildcard can have only one bound, either a lower or an upper
bound. A list of wildcard bounds is not permitted.
A type parameter, in constrast, can have several bounds, but there is
no such thing as a lower bound for a type parameter. |
LINK TO THIS
|
TypeArguments.FAQ203
|
REFERENCES
|
What
is a wildcard?
What
is a wildcard bound?
What
is a type parameter?
What
is a type parameter bound?
Why
is there no lower bound for type parameters?
|
CONTENT
PREVIOUS
NEXT
INDEX
|