January 23, 2023

Java Generics - WildCards

While writing generic code you can also use a question mark (?) as type which represents an unknown type and known as wildcard in Java generics.

WildCard in Java generics and class relationship

You can use wildcards to create a relationship between generic classes or interfaces.

In case of non-generic classes

class A { /* ... */ }
class B extends A { /* ... */ }

You can assign reference of child class to parent class.

B b = new B();
A a = b;

But the same assignment does not apply to generic types.

List<B> lb = new ArrayList<>();
List<A> la = lb;   // compile-time error

So, List<B> is not the subtype of List<A> even if A is the parent class.

You can also understand it using the list of integers (List<Integer>) and list of numbers (List<Number>) where Number is the parent class of Integer but List<Integer> is not a subtype of List<Number> in fact, these two types are not related. The common parent of List<Number> and List<Integer> is List<?>. List of unknown type that could be a List<Integer>, List<A>, List<String> and so on.

Using this knowledge of common parent of two generic classes we’ll see how to create a bounded relationship between two generic classes (or interfaces) using three types of wildcards.

Types of wildcards in Java Generics

Based on the limit you want to impose on the relationship between two generic classes there are three types of wildcards.

  • Upper bounded wildcards
  • Lower bounded wildcards
  • Unbounded wildcards

Upper bounded wildcards

To declare an upper-bounded wildcard, use the wildcard character ('?'), followed by the extends keyword, followed by the type that acts as upper bound. Upper bound wildcard matches the upper bound type or any of its subclasses.

For example List<? extends Number> matches a list of type Number or any of its subclasses i.e. List<Integer>, List<Double>, List<Number>.

Upper bounded wildcard Java example

Suppose you want to write a method that can add all the elements of the passed list. Since you have to add the elements so List should have elements of type Integer, Float, Double since Number is the super class for all these wrapper classes so you can create an upper bound using Number class.

import java.util.Arrays;
import java.util.List;

public class WildCard {
  public static void main(String[] args) {
    List<Integer> li = Arrays.asList(1, 2, 3, 4);
    System.out.println("sum = " + addListElements(li));
    //List<Double>
    List<Double> ld = Arrays.asList(1.1, 2.2, 3.3, 4.4);
    System.out.println("sum = " + addListElements(ld));
  }
    
  public static double addListElements(List<? extends Number> list){
    double s = 0.0;
    for (Number n : list) {
      s += n.doubleValue();
    }
    return s;
  }
}
Output
sum = 10.0
sum = 11.0

Lower bounded wildcards

A lower bounded wildcard is expressed using the wildcard character ('?'), following by the super keyword, followed by its lower bound. For example

<? super A>

A lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type. For example you want to write a method that works on lists of Integer and the supertypes of Integer, such as Integer, Number, and Object then you would specify a lower bounded wildcard like this-

List<? super Integer>

Lower bounded wildcards Java example

Suppose you want to write a method that can insert integers to the end of a list and that can be a List of Object, List of Number or List of Integer then you can create a lower bound using Integer class.

import java.util.ArrayList;
import java.util.List;

public class WildCard {
  public static void main(String[] args) {
    // with List<Object>
    List<Object> lo = new ArrayList<Object>();
    insertNumbers(lo);
    
    // with List<Number>
    List<Number> ln = new ArrayList<Number>();
    insertNumbers(ln);
    
    // with List<Integer>
    List<Integer> li = new ArrayList<Integer>();
    insertNumbers(li);
  }
    
  public static void insertNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
      list.add(i);
    }
    System.out.println("Elements in List- " + list);
  }
}
Output
Elements in List- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Elements in List- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Elements in List- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Unbounded wildcards in Java generics

The unbounded wildcard type is specified using the wildcard character (?).

For example, List<?> represents a list of unknown type.

Unbounded wildcard Java example

Suppose you want to write a method that can print elements of a List of any type then you should use List<?> as method argument. Using List<Object> won’t work as List<Integer>, List<String>, List<Double> are not subtypes of List<Object>.

import java.util.Arrays;
import java.util.List;

public class WildCard {
  public static void main(String[] args) {
    // With List<Integer>
    List<Integer> li = Arrays.asList(5, 6, 7);
    printListElements(li);
    // With List<Double>
    List<Double> ld = Arrays.asList(1.2, 3.8, 8.2);
    printListElements(ld);
  }
    
  public static void printListElements(List<?> list){
    for (Object e : list){
      System.out.print(e + " ");
    }
    System.out.println();
  }
}
Output
5 6 7 
1.2 3.8 8.2 
That's all for the topic Java Generics - WildCards. If something is missing or you have something to share about the topic please write a comment.
You may also like

No comments:

Post a Comment