There may be times when you want to restrict the types that can be used as type parameters.
For example, a method that operates on numbers might only want to accept instances of Number
or its subclasses. The Number
class is the superclass of classes BigDecimal
, BigInteger
, Byte
, Double
, Float
, Integer
, Long
, and Short
.
To declare a bounded type parameter, list the type parameter's name, followed by extends
, followed by its upper bound.
<T extends Number> Number maximum(T number1, T number2, T number3){
// ...
}
Here, extends
is used in a general sense to mean either
extends
as in classes orimplements
as in interfaces.This method:
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // assume x is initially the largest
if (y.compareTo(max) > 0)
max = y; // y is the largest so far
if (z.compareTo(max) > 0)
max = z; // z is the largest
return max; // returns the largest object
}
becomes:
public static Comparable maximum(Comparable x, Comparable y, Comparable z) {
Comparable max = x;
if (y.compareTo(max) > 0)
max = y;
if (z.compareTo(max) > 0)
max = z;
return max;
}
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(10, 20, 30, 40);
System.out.println("Max integer: " + getMax(integers));
List<Long> longs = Arrays.asList(10L, 20L);
System.out.println("Max long: " + getMax(longs));
List<Double> doubles = Arrays.asList(10.1, 10.2, 10.3, 10.4);
System.out.println("Max double: " + getMax(doubles));
}
You can find the solution to this exercise here.
In generic code, the question mark ?
, called wildcard, represents an unknown type.
The unbounded wildcard type is specified using the wildcard character (?
), for example, List<?>
.
There are two scenarios where an unbounded wildcard is a useful approach.
Object
class.List.size
or List.clear
.Here is an example of a method that can be implemented using Object::toString()
.
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 objects, i.e., List<Object>
. It cannot print List<Integer>
, List<String>
, List<Double>
, because they are not subtypes of List<Object>
.
Instead, if we use a wildcard, it can handle all of these cases.
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
Example from this StackOverflow answer.
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>
, andList<Number>
.You can achieve this by using an upper bounded wildcard.
List<? extends Number>
But what is the difference between the following expressions?
List<? extends Number>
List<T extends Number>
You declare a T
so that you can refer to it again.
Here is an example in which it is useful to use upper-bounded wildcards.
List<Person> merge( List<? extends Person> list1, List<? extends Person> list2) {
List<Person> merged = new ArrayList<>();
merged.addAll(list1);
merged.addAll(list2);
return merged;
}
Which can later be used as:
List<Person> people = merge(new ArrayList<Person>(), new ArrayList<Student>() );
or as:
List<Person> people = merge(new ArrayList<Student>(), new ArrayList<Teacher>() );
None of the above would have worked if the collect method had been declared as:
List<Person> merge(List<Person> list1, List<Person> list2)
Example from this StackOverflow answer.
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 anything that can hold Integer values, that is:
List<Integer>
,List<Number>
, andList<Object>
.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>.
<T>
vs. <?>
(1)Sometimes, wildcards and type parameters do the same thing, but for certain purposes, you will need type parameters to:
Suppose you want to ensure that the src
and dest
lists passed to copy()
have the same type.
You can do it with type parameters.
public static <T extends Number> void copy(List<T> dest, List<T> src)
Making it safe to copy elements from src
to dest
.
If you were to use a wildcard instead:
public static void copy(List<? extends Number> dest, List<? extends Number> src)
You would be able to pass List<Integer>
and List<Float>
as dest
and src
, making it unsafe to move elements from src
to dest
.
<T>
vs. <?>
(2)Another big difference is that <T>
allows you to refer to "T" within the method as if the concrete class was given.
public <T extends Animal> void takeThing(ArrayList<T> list){
T first = list.get(0);
// ...
}
If you had used a wildcard instead, you would not be able to do that.
public void takeThing(ArrayList<? extends Animal> list){
// ...
}