WORKING WITH BOUNDED WILDCARDS IN JAVA

Java Generics is one of the most important feature in java. It helps to write and use parameterized types. It also provide type-safety and compile-time checking. In this tutorial, we will learn how to use bounded parameter type and wildcard generic typewhich are both related to Java Generics. I am assuming that you knew the basics about Generics in Java.

Wildcards

A wildcard generic type ia an unknown generic type represented with a question mark ?. Most of the time, we use wildcard when we are working with collections. Let’s consider the following method:

public static <T> void printSize(Collection<T> collection) {
    System.out.println("The size of the Collection is "+collection.size());
}

This method takes as input a Collection object and print its size. By looking at the method definition, We can notice that the generic parameter T occurs only once in the method signature and the name T never appears in the method body. In this case, we can use wildcard to have cleaner and easy to grasp method definition.

public static void printSize(Collection<?> collection) {
    System.out.println("The size of the Collection is "+collection.size());
}

Wildcard comes some restrictions that we will see in the rest of the tutorial.

Bounded Wildcards

Java treats generics as Objects and therefore when working with generics, we don’t have many methods available. Bounded wildcards solve this by restricting what types can be used in the Parameter position. A bounded parameter type can be defined as a generic type that specifies a bound for the generic.

We have three types of bounded wildcard:

  • Unbounded wildcard
  • Upper-Bounded Wildcard
  • Lower-Bounded Wildcard

Unbounded Wildcards

An Unbounded wildcard represents any data type. The following code show an example of using the Unbounded wildcard.

public static void printContent(List<?> list) {
    for (Object o : list)
        System.out.print(o+" ");
    System.out.println();
}

public static void main(String[] args) {
    // Call printContent with a list of integer
    List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    printContent(list1);

    // Call printContent with a list of String
    List<String> list2 = Arrays.asList("Paris", "Beijing", "Rome", "New York");
    printContent(list2);
}

Output:

1 2 3 4 5 6 7 
Paris Beijing Rome New York 

There is one important restriction when using Unbounded wildcard with collections: the collection becomes logically immutable. Immutable means that the collections cannot be modified. In order words, we cannot remove or add item to the collection.

Upper-Bounded Wildcards

The syntax of Upper-Bounded Wildcards is ? extend A. The upper-Bounded Wildcard says that any class that extends A or A itself can be used as the formal parameter type.

To understand how to use upper-bounded wildcards, let’s write a method that adds up the total of a list of numbers. We want would like our method to take many type of number : integer, long, float … In this case, we can use the upper-bounded wildcard as follow:

public static double total(List<? extends Number> list) {
    double sum = 0.0;
    for (Number number : list)
        sum += number.doubleValue();
    return sum;
}

public static void main(String[] args) {
    // Call total with a list of integer
    List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    
    System.out.println("The total of list1 is " + total(list1));
    
    // Call total with a list of float
    List<Float> list2 = Arrays.asList(1.2f, 7.4f, 2f, 4.5f);
    System.out.println("The total of list2 is " + total(list2));
}

Output:

The total of list1 is 28.0
The total of list2 is 15.100000143051147

Like unbounded wildcards, when using upper-bounded wildcards with collections, the collection becomes logically immutable.

Lower-Bounded Wildcards

We have seen that when using Unbounded wildcard and upper-bounded wildcardswith collections, the collection becomes logically immutable and we cannot modified the collection. Lower-Bounded Wildcards solve this by allowing to add and remove items to the collection.

The syntax of Lower-Bounded Wildcards is ? super A. With a lower bound, we are telling Java that the any that is a superclass of A or A itself can be used as the formal parameter type.

To understand Lower-Bounded Wildcards, let’s implement a method that as input a list and removes the first element of the list and adds the value 50 to the back of the list and print it. We would like our method works with a list of Integer and also a list of Number.

public static void removeAndAdd(List<? super Integer> list) {
    list.remove(0);
    list.add(50);
    for (Object obj : list)
        System.out.print(obj.toString()+" ");
    System.out.println();
}

public static void main(String[] args) {
    // Call total with a list of integer
    List<Integer> list1 = new ArrayList<>();
    list1.add(1); list1.add(2); list1.add(3);
    list1.add(4); list1.add(5); list1.add(6);
    removeAndAdd(list1);
    
    // Call total with a list of Number
    List<Number> list2 = new ArrayList<>();
    list2.add(1.2); list2.add(7.4); list2.add(3);
    list2.add(4.5); list2.add(2.7); list2.add(18);
    removeAndAdd(list2);
}

Output:

2 3 4 5 6 50 
7.4 3 4.5 2.7 18 50 

We can also call the method removeAndAdd with a list of Object because Object is also a superclass of Integer.

Now when should we use the upper-Bounded Wildcard and when should we use the lower-Bounded Wildcard? Joshua Bloch in Effective Java book has suggested Producer extends, Consumer super mnemonic regarding the use of bounded wildcards. That means if type T is used as a producer than use <? extends T> and if type T represent consumer than use <? super T> bounded wildcards.

Thanks for reading. Please leave feedback and questions in the comments!

You May Also Like

About the Author: Miguel KAKANAKOU

Leave a Reply

WP to LinkedIn Auto Publish Powered By : XYZScripts.com