CS2030 AY19/20 Semester 2
  • Introduction
  • Textbook Contributions
  • Peer Learning Tasks
  • Piazza Participation
  • Setting Up Lab Environment
  • Setting Up Java
  • Setting Up Vim
  • Setting Up MacVim
  • Setting Up Sunfire and PE Nodes
  • Setting Up Checkstyle
  • Textbook
  • CS2030 Java Style Guide
  • CS2030 Javadoc Specification
  • JDK 11 Download Link
  • Codecrunch
  • Github Repo
  • Piazza Forum

  • bounded Wildcards


    Edit the material here!

    This was adapted from The Java tutorial and Question 445 from Piazza.

    upper-bounded wildcard

    <? extends T> is known as the upper-bounded wildcard. This is used to relax the use of complex type to its co-variant complex types, i.e.complex types with type parameter being subtype of T. So, List<? extends T> means a List of subtypes of T. Short example: consider the following process method:

    public static void process(List<? extends T> myList) {
    for (T ele : myList) {
    // ...
    }
    }

    myList is the a list of T or subtype of T, thus no matter what runtime-type ele is, we can always assign compile time type T to it.

    Notice, however, that it is better not to initialize a list with an upper-bounded wild card.

    ArrayList<? extends Number> list = new ArrayList<>();
    list.add(1);

    The code above will actually give rise to an error of incompatible types. One possible explanation is that the type parameter of list is not determined, which can be of type Integer, Double, or any other subtypes of Number. Since it is possible for the type to be of type Double, storing an integer into the list will be invalid as an integer is not a double value. The same applies for the reason why a double value cannot be added to the list.

    lower-bounded wildcard

    Similarly, <? super T> is known as the lower-bounded wildcard, which relaxes the use of complex type to its contra-variant complex types, i.e.complex types with type parameter being supertype of T. So, List<? super T> means a List of supertypes of T. Short example: consider the following adding method:

    public static void adding(List<? super T> myList) {
    myList.add(new T());
    }

    myList is the a list of T or supertype of T, thus it is always compatible to add object of type T into it since T is the subtype of supertype of T

    Guidelines for Wildcard use

    One of the more confusing aspects when learning to program with generics is determining when to use an upper bounded wildcard and when to use a lower bounded wildcard. This page provides some guidelines to follow when designing your code.

    For purposes of this discussion, it is helpful to think of variables as providing one of two functions:

    An "In" Variable An "in" variable serves up data to the code. Imagine a copy method with two arguments: copy(src, dest). The src argument provides the data to be copied, so it is the "in" parameter. An "Out" Variable An "out" variable holds data for use elsewhere. In the copy example, copy(src, dest), the dest argument accepts data, so it is the "out" parameter.

    Of course, some variables are used both for "in" and "out" purposes — this scenario is also addressed in the guidelines.

    You can use the "in" and "out" principle when deciding whether to use a wildcard and what type of wildcard is appropriate. The following list provides the guidelines to follow:

    Wildcard Guidelines:

    • An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
    • An "out" variable is defined with a lower bounded wildcard, using the super keyword.
    • In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
    • In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.

    More quetions and exercises on Java tutorial