What Is The Meaning of "?" in Java?

What Is The Meaning of "?" in Java?

·

4 min read

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 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:

  1. You have a method that takes in a list of unknown types.

  2. In the main method, you decide the unknown type will be an Integer.

  3. In the end, you populate the list and print out the result.

You can replace the ? with a Double, Float, Short, String, etc. 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 saying that a wildcard is bounded, and it means that ? uses the extends keyword. When you use the extends keyword in the wildcard, you say you 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:

  1. We have a method that prints the list of cards. The only information you know when creating the method is that ? can be replaced with a subclass of the Object class.

  2. In the main method, you decide that ? is going to extend the Integer and the String class.

  3. In the end, you populate the list and print out the result.

Lower bound class

A lower-bound wildcard makes use of the super keyword. In Java, you use super to relate to parent class objects. A wildcard that uses the super keyword means that it can accept any super type. For example, the supertype 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:

  1. We have a method that prints a list of cards. This method takes in an unknown type whose supertype is Integer.

  2. In the main method, we decide to replace ? with Object because Object is the parent of all classes, Integer included.

  3. In the end, we populate the list and print out the result.

Where should we use wildcards?

We can use wildcards on return type, local variable, and as a type of parameter.

What are the benefits of using wildcards?

Generics are great because of the following:

  1. Better compile-time checking: The compiler will tell you if you use an Object type different from the one you specified.

  2. 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.

  3. It's great for data structures and algorithms: ArrayList and Hashmap are a couple of examples that use Generic.

In addition, we don't need to typecast an object with Generics, and we ensure that the types we decided to instantiate are compatible.

Conclusion

I hope that you've understood the meaning of ? and Generic programming in Java.

Thank you for reading! 😊

Did you find this article valuable?

Support Maddy by becoming a sponsor. Any amount is appreciated!