In Java Generic programming, ? refers to a wildcard. If you're wondering what is a wild card, thinking about card games is a great way to understand its meaning.
According to Cambridge Dictionary, a wild card is:
A playing card that does not have any particular value but that can be used to represent any other card.
In short, the question mark in Java represents a type that is initially uncertain, but its type is revealed after when we declare it.
In Java, there are three types of wildcards:
- unbounded wildcard
- upper bound wildcard
- lower bound wildcard
UNBOUNDED WILDCARD
? on its own is an unbounded wildcard, because there is no restriction on the type that you can associate with it.
Let's look at a simple example to understand how we can use ?:
package Generics;
import java.util.ArrayList;
import java.util.List;
public class Card {
public void printNumbers(List<?> listOfNumbers){
System.out.println("The possible numbers in the cards are " + listOfNumbers);
}
public static void main(String[] args) {
Card card = new Card();
List<Integer> cardNumbers = new ArrayList<>();
cardNumbers.add(2);
cardNumbers.add(3);
cardNumbers.add(4);
cardNumbers.add(5);
card.printNumbers(cardNumbers);
}
}
The outcome is:
The possible numbers in the cards are [2, 3, 4, 5]
In the example above:
- We have a method that takes in a list of unknown types.
- In the main method, we decide that the unknown type is going to be an Integer.
- In the end, we populate the list and print out the result.
We can replace the ? with a Double, Float, Short, String and so on. It has to be a type that extends the java.lang package for it to work, otherwise, you'll get a compilation error.
UPPER BOUND WILDCARD
This is the same as to say that a wildcard is bounded, and it means that ? uses the *extends * keyword. When we use the extends keyword in the wildcard, we are saying that we want the wildcard to be a subclass Object type. An example of an Object subclass can be Number (which is the superclass of Integer, Double, Float, and so on) or String.
Let's look at this example:
package Generics;
import java.util.ArrayList;
import java.util.List;
public class Card {
public void printCards(List<? extends Object> listOfCards){
System.out.println("The accepted cards are " + listOfCards);
}
public static void main(String[] args) {
Card card = new Card();
// using Integer
List<Integer> cardNumbers = new ArrayList<>();
cardNumbers.add(2);
cardNumbers.add(3);
card.printCards(cardNumbers);
// using String
List<String> cardNames = new ArrayList<>();
cardNames.add("Hearts");
cardNames.add("Clubs");
card.printCards(cardNames);
}
}
The outcome is:
The accepted cards are [2, 3]
The accepted cards are [Hearts, Clubs]
In the example above:
- We have a method that prints the list of cards. The only information that we know when we create the method is that ? can be replaced with a subclass of the Object class.
- In the main method, we decide that ? is going to extend the Integer and the String class.
- In the end, we populate the list and print out the result.
LOWER BOUND CLASS
A lower bound wildcard makes use of the super keyword. In Java, we use "super" to relate to parent class objects. A wildcard that uses the super keyword means that it can accept any type of super type. For example, the super type Integer can be Number and Object.
Let's look at an example to understand this better:
package Generics;
import java.util.ArrayList;
import java.util.List;
public class Card {
public void printCards(List<? super Integer> listOfCards){
System.out.println("The accepted cards are " + listOfCards);
}
public static void main(String[] args) {
Card card = new Card();
List<Object> cardNames = new ArrayList<>();
cardNames.add("Diamonds");
cardNames.add("Spades");
card.printCards(cardNames);
}
}
The outcome is:
The accepted cards are [Diamonds, Spades]
In the example above:
- We have a method that prints a list of cards. This method takes in an unknown type whose super type is Integer.
- In the main method, we decide to replace ? with Object, because Object is the parent of all classes, Integer included.
- In the end, we populate the list and print out the result.
WHERE CAN USE WILDCARDS?
We can use wildcards:
✅ At return type.
✅ At local variable.
✅ As a type of parameter.
WHAT ARE THE BENEFITS OF USING WILDCARDS?
In one of my previous posts about the meaning of "T" in Java, I discussed that Generics are great because of:
- Better compile-time checking: if you use an Object type different from the one that you specified, the compiler will tell you.
- Reusability: you can use a class, method or interface multiple times, because you decide which Object type to apply based on what you're trying to achieve.
- It's great for data structures and algorithms: ArrayList and Hashmap are a couple of examples that make use of Generic.
In addition, with Generics we don't need to typecast an object, and we ensure that we the types that we decided to instantiate are compatible.
I hope that you've understood the meaning of ? and Generic programming in Java.
Thank you for reading! 😊
Originally published on Tech with Maddy.