We can use wildcard arguments with type parameters in Java to represent unknown types. A wildcard is a symbol that can represent any type. This is useful when we don't know the type of an object in advance or when we want to allow for multiple types. For example, consider the following code, which declares a list of strings:
List<String> list = new ArrayList<>();
In the code above, the list variable can only store String
objects. In this answer, we will learn about the following wildcard arguments:
?
: This is used for unknown types. It can further be bounded using these arguments:?
super
?
extends
&
: This is used for combining multiple constraints.We can use a wildcard argument as follows to make this code flexible:
List<?> list = new ArrayList<>();
In the code above, the list variable can store objects of any type.
Java also allows us to use wildcard arguments in type parameters. For example, the following code defines a generic class that takes a list of any type:
import java.util.*;class WildcardExample {public static void printList(List<?> list) {for (Object o : list) {System.out.println(o);}}public static void main( String args[] ) {List<String> list1 = new ArrayList<>();list1.add("Hello");list1.add("World");List<Integer> list2 = new ArrayList<>();list2.add(1);list2.add(2);printList(list1);printList(list2);}}
printList()
method that takes a list of any type. We can use this method to print out the elements in the list.list1
and add two strings to the list.list2
and add two integers to the list.printList()
method with a list of strings to print out each string in the list, as follows:HelloWorld
printList()
method with a list of integers to print out each integer element in the list, as follows:12
We can also specify an upper bound for the wildcard argument, as in the following:
List<? super Number> list = new ArrayList<>();
In the code snippet above, the list variable can only store objects of type number
or it's supertypes.
import java.util.*;class WildcardExampleSuper {public static void printList(List<? super Integer> list) {for (Object o : list) {System.out.println(o);}}public static void main( String args[] ) {List<Object> list1 = new ArrayList<>();list1.add("Hello");list1.add(3);printList(list1);}}
list1
of type Object
and add two objects to the list.printList()
method with a list of objects to print out each element in the list.We can also specify a lower bound for the wildcard argument, as in the following:
List<? extends Number> list = new ArrayList<>();
In the above code snippet, the list variable can only store objects of type Number
or its subtypes.
In the following code, we define a generic class that takes a list of a type that extends a Number
class:
import java.util.*;class WildcardExample {public static void printList(List<? extends Number> list) {for (Object o : list) {System.out.println(o);}}public static void main( String args[] ) {List<Integer> list2 = new ArrayList<>();list2.add(1);list2.add(2);printList(list2);}}
printList()
method that takes a list of a type that extends a Number
class. We can use this method to print out the elements in the list.list2
and add two integers to the list.printList()
method with a list of integers to print out each integer element in the list, as follows:12
We can specify multiple types of constraints by using the &
operator. For example, the following code defines a generic class that takes a list of objects that are both Comparable
and Serializable
:
List<T extends Comparable & Serializable> list = new ArrayList<>();
The following code defines a generic class with multiple type constraints:
import java.io.Serializable;import java.util.*;class Shape implements Serializable, Comparable{String name;Shape(String name) {this.name = name;}@Overridepublic int compareTo(Object o) {return 0;}@Overridepublic String toString() {return "Shape {name = " + this.name + "}";}}class MultipleTypeConstraints <T extends Comparable & Serializable> {public void printList(List<T> list) {for (T t: list) {System.out.println(t);}}public static void main(String[] args) {MultipleTypeConstraints<Shape> instance = new MultipleTypeConstraints<>();List<Shape> list = new ArrayList<>();Shape circle = new Shape("circle");Shape square = new Shape("square");list.add(circle);list.add(square);instance.printList(list);}}
Shape
that implements both Serializable
and Comparable
interfaces.compareTo()
method of the Comparable
interface. toString()
method to print a user-friendly message.MultipleTypeConstraints
that takes a type parameter T
with multiple type constraints. The type parameter T
should extend (or implement) both Comparable
and Serializable
interfaces.printList()
that takes a list of objects of type T
and prints them.MultipleTypeConstraints
.Shape
objects.Shape
class with object name as circle
. We passed "circle"
as the name
parameter to its constructor.Shape
class with object name as square
. We passed "square"
as the name
parameter of its constructor.Shape {name = circle}Shape {name = square}
In this Answer, we learned about wildcards arguments in Java and how to use them. We also saw how to specify multiple type constraints by using the &
operator.