Wildcards and Bounded Types in Java

Java's generics feature allows us to write more type-safe and efficient code. One important aspect of generics is the use of wildcards and bounded types. These features provide flexibility when working with parameterized types. In this article, we will explore wildcards and bounded types in Java and learn how to effectively utilize them.

Wildcards

Wildcards are denoted by the ? symbol and allow us to write generic code that operates on unknown types. There are three types of wildcards in Java: ?, ? extends T, and ? super T.

Unbounded Wildcard (?)

The unbounded wildcard ? represents an unknown type. It is commonly used when we want to write generic code that can accept and operate on any type.

For example, consider a method that prints elements of a List:

public static void printList(List<?> list) {
    for (Object element : list) {
        System.out.println(element);
    }
}

In the above code, the printList method can accept a List of any type. We can pass a List<Integer>, List<String>, or any other List to this method.

Upper Bounded Wildcard (? extends T)

The upper bounded wildcard ? extends T represents an unknown type that is a subtype of T. It allows us to work with a collection of objects of a specific supertype.

For instance, imagine a method that calculates the sum of numbers in a List:

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

Here, the sum method accepts a List of any type that extends Number. We can pass a List<Integer>, List<Double>, or any other List that contains numeric types.

Lower Bounded Wildcard (? super T)

The lower bounded wildcard ? super T represents an unknown type that is a supertype of T. It allows us to work with a collection of objects that are of a specific subtype.

Consider a method that adds elements to a List:

public static void addToList(List<? super Integer> list) {
    list.add(42);
    list.add(13);
}

In this example, the addToList method accepts a List of any type that is a supertype of Integer. We can pass a List<Object>, List<Number>, or any other List that can hold Integer or its supertypes.

Bounded Types

Bounded types are used to restrict the types that can be used as type arguments in generics. There are two types of bounded types in Java: upper bounded type and lower bounded type.

Upper Bounded Type

An upper bounded type allows us to specify that a type argument must be a subtype of a specific class or implement a specific interface.

For example, consider a generic method that finds the maximum element in a collection of comparable objects:

public static <T extends Comparable<T>> T findMax(Collection<T> collection) {
    T max = collection.iterator().next();
    for (T element : collection) {
        if (element.compareTo(max) > 0) {
            max = element;
        }
    }
    return max;
}

In this method, we use an upper bound <T extends Comparable<T>> to ensure that the type argument T is a subtype of Comparable<T>. Hence, we can compare the elements and find the maximum.

Lower Bounded Type

A lower bounded type allows us to specify that a type argument must be a supertype of a specific class.

For instance, consider a generic method that appends elements to a list:

public static void appendToList(List<? super String> list, String element) {
    list.add(element);
}

In this example, we use a lower bound <? super String> to ensure that the type argument is a supertype of String. This allows us to add String and its subtypes to the list.

Conclusion

Utilizing wildcards and bounded types in Java generics allows us to write more flexible and practical code. Wildcards provide a way to represent unknown types or specific subtypes/supertypes, while bounded types restrict the types that can be used as type arguments. By mastering these features, we can develop more effective and reusable generic code.


noob to master © copyleft