In this section, we will learn what the wildcard is and how it works in Java.
What is Java Wildcard?
Sometimes we have a variable of type generic, but we don’t have an object to assign to it.
In a situation like this, when we don’t know what the final object to a variable (parameters as well) will be, we can set the question mark `?` as the `type parameter` in the angle brackets.
The question mark `?` is called wildcard. This means the `type parameter` of the target variable is unknown.
Java generic wildcard example:
import java.util.ArrayList; public class Simple { public static void printList(ArrayList<?> arrayList){ System.out.println(arrayList); } public static void main(String[] args) { ArrayList<String>arrayList = new ArrayList<>(); arrayList.add("One"); arrayList.add("Two"); arrayList.add("Three"); printList(arrayList); } }
Output:
[One, Two, Three]
Note: the `ArrayList` is a generic type and it can be used to store elements into an object of this type. (You’ll learn more about this class in ArrayList section).
Here the `check` method has one parameter, and that is of type `ArrayList<?>`. The type of the parameter denotes that the parameter accepts any object of type `ArrayList` with any `type parameter` set for it.
Types of wildcard in Java:
There are three types of wildcard in Java:
- Unbound wildcard
- Upper bound wildcard (extends wildcard)
- Lower bound wildcard (super wildcard)
Java Unbound Wildcard
Basically, when using the `?` mark in the angle brackets, the compiler considers the target variable as unbound. This means the variable accepts objects of the generic type with any `type parameter`. If you think about it, this action is somewhat raw and wild! That’s why they call it wildcard. Generally , wildcard means unpredictable, with no defined role!
For example, here the type parameter of the object that we sent to the `check` method was `String` but we could change the type and use others and send them to the `check` method without any problem.
Example: Creating unbound wildcard
import java.util.ArrayList; public class Simple { public static void printList(ArrayList<?> arrayList){ System.out.println(arrayList); } public static void main(String[] args) { ArrayList<Integer>arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); printList(arrayList); } }
Output:
[1, 2, 3]
Java Upper bound wildcard (extends wildcard)
When we use wildcards, we’re basically allowing any object of the target generic class with any `type parameter` to be assigned. But sometimes we have boundaries!
For example, we might only accept those objects that have a type parameter of either `Number` type or a subclass that derives from the `Number` class. In such a case, we can use `upper bound wildcard`.
Here’s how it works:
<? extends class/interface name>
Note: after the question `?` mark we use the keyword `extends` and followed by that we put the interface or the class name. This means the target `type parameter` should be either the same as the class/interface we defined here or be a type that derived from the class/interface that is defined here in the angle brackets.
Example: Creating upper bound wildcard
public class Simple { public static void printList(ArrayList<? extends Number> arrayList){ System.out.println(arrayList); } public static void main(String[] args) { ArrayList<Integer>arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); printList(arrayList); } }
Output:
[1,2,3]
The parameter of the `printList` method is of type `ArrayList<? extends Number>`. This means the parameter only accepts objects that their type parameter is either of type `Number` or a subclass of the `Number` class.
So here, because the `Integer` class is a subclass of the `Number` class, we could pass an object of this type to the `printList` method.
Java extends wildcard example:
import java.util.ArrayList; public class Simple { public static void printList(ArrayList<? extends Number> arrayList){ System.out.println(arrayList); } public static void main(String[] args) { ArrayList<String>arrayList = new ArrayList<>(); arrayList.add("check"); arrayList.add("two"); arrayList.add("Apple"); printList(arrayList); } }
Output:
Error:(15, 19) java: incompatible types: java.util.ArrayList<java.lang.String> cannot be converted to java.util.ArrayList<? extends java.lang.Number>
Here we got an error, and that’s because we tried to pass an object with a type parameter of `String` which is not a subclass of the `Number` class.
Java Lower bound Wildcard (super wildcard)
Lower bound wildcard is another way of putting boundary on the range of objects that can be passed to a parameter of type wildcard (unknown).
Let’s see the structure:
<? super class/Interface name>
Here after the question `?` mark we used the keyword `super` and followed by that is the name of the class/interface.
When using a lower bound wildcard, that means the target object should have a type parameter that either is the class/ interface that we defined or be a parent of this type in the angle brackets.
Example: Creating lower bound wildcard
import java.util.ArrayList; public class Simple { public static void printList(ArrayList<? super Integer> arrayList){ System.out.println(arrayList); } public static void main(String[] args) { ArrayList<Integer>arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); printList(arrayList); } }
Output:
[1,2,3]