DEV Community

Peng-Yu Chen
Peng-Yu Chen

Posted on

Java Wildcards Introduction

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

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)

  • Lower bound: void fun(List<? super Animal> list)
    This input list is parameterized over at least type Animal (can be any supertype of Animal)

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

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)