Java Wildcards Introduction
Tags: java
Published: 2020-12-08 20:33:31

## Introduction

The wildcard mark (`?`) can help us write a program in a more explicit way.

It's used to represent an unknown type and is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.

## Example

Let's assume we have an object hierarchy:

- The `Animal` class inherits from `Object` trivially
- The `Dog` and `Cat` classes both inherit from `Animal`
- The `GoldenRetriever` class inherits from `Dog`

<img src="https://dev-to-uploads.s3.amazonaws.com/i/2zfg4qf68xu5fwv4xkef.png"  width="300" height="300">

In general, there're 3 ways to use a wildcard:

- Upper bound: `void fun(List<? extends Animal> list)`
  This input list is parameterized over **at most** type `Animal` (can be any subtype of `Animal`)
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/o5g1jjaes4z2cry1334i.png"  width="300" height="300">

- Lower bound: `void fun(List<? super Animal> list)`
  This input list is parameterized over **at least** type `Animal` (can be any supertype of `Animal`)
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/ncon0i6dgd11h9u64sfv.png"  width="300" height="300">

- Unbounded: `void fun(List<?> list) { list.clear(); }`

  - Here we don't care what the type of the list is since `clear()` isn't type dependent.
  - If we were supposed to use only methods defined in `Object`, then unbounded wildcard works fine.

## Java Demo Code

> See the following code, `WildcardDemo.java`, for better understanding.

First, try to compile it with `javac WildcardDemo.java` and see the error console :)

Second, to successfully compile the code, note that there're 2 lines marked as `// X (can't compiled)`, you should comment them in order to compile the code.

*The code is fully commented. Feel free to leave any comment if you're not understanding.*

```java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Our Object Hierarchy:
 *
 *          Object
 *            |
 *          Animal
 *           / \
 *        Dog   Cat
 *         |
 *  GoldenRetriever
 */
public class WildcardDemo {

  private class Animal {}
  private class Dog extends Animal {}
  private class Cat extends Animal {}
  private class GoldenRetriever extends Dog {}

  // `animals` is parameterized over "at most" type `Animal`
  private void funExtends(List<? extends Animal> animals) {
    // Can't add a Dog (subtype) to the `animals` because it may
    // contain something "lower than an Animal".
    // For example, it might be `List<GoldenRetriever>` or `List<Cat>`
    animals.add(new Dog()); // X (can't compiled)

    // Can't put anything into a type declared with an extends
    // wildcard except for the value `null`, which is a subtype
    // of every reference type
    animals.add(null);

    // Can retrieve an `Animal`, because any subtype of `Animal`
    // must be an `Animal`
    Animal animal = animals.get(0);

    System.out.println(animal.getClass().toString());
  }

  // `animals` is parameterized over "at least" type `Animal`
  private void funSuper(List<? super Animal> animals) {
    // Can add a `Dog` (subtype) to the `animals` because a `Dog`
    // is guaranteed an `Animal` or any supertype of an `Animal`
    animals.add(new Dog());

    // Can't get anything out from a type declared with a super
    // wildcard except for a value of type `Object`, which is a
    // super type of every reference type
    Animal animal = animals.get(0); // X (can't compiled)
    Object object = animals.get(1);

    System.out.println(object.getClass().toString());
  }

  private List<Animal> getAnimals() {
    return new ArrayList<>(Arrays.asList(
        new GoldenRetriever(),
        new Cat()
      )
    );
  }

  public static void main(String[] args) {
    WildcardDemo demo = new WildcardDemo();
    List<Animal> animals = demo.getAnimals();
    demo.funExtends(animals);
    demo.funSuper(animals);
  }
}
```