Java Generics - Wildcards

Introduction

Wildcards in Java Generics provide a way to handle unknown types. They are represented by the question mark (?) and can be used in various contexts, such as method parameters, return types, and field types. Wildcards enable greater flexibility in the code, allowing it to work with different types without knowing them explicitly.

Table of Contents

  1. What are Wildcards?
  2. Types of Wildcards
    • Unbounded Wildcards
    • Bounded Wildcards
      • Upper Bounded Wildcards
      • Lower Bounded Wildcards
  3. Use Cases for Wildcards
  4. Example: Using Wildcards
  5. Guidelines for Using Wildcards
  6. Conclusion

1. What are Wildcards?

Wildcards represent unknown types in generics. They allow you to specify that a type parameter can be substituted with any type. Wildcards are especially useful when you want to write code that can handle multiple types without needing to specify the exact type.

2. Types of Wildcards

Unbounded Wildcards

Unbounded wildcards can represent any type. They are declared using a single question mark (?).

Syntax:

List<?> list = new ArrayList<>();

Example:

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

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

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        stringList.add("World");

        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        printList(stringList);
        printList(intList);
    }
}

Output:

Hello
World
1
2

Upper Bounded Wildcards

Upper bounded wildcards restrict the unknown type to be a specific type or a subtype of that type. They are declared using the extends keyword.

Syntax:

List<? extends Number> list = new ArrayList<>();

Example:

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

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

    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);
        intList.add(3);

        List<Double> doubleList = new ArrayList<>();
        doubleList.add(1.1);
        doubleList.add(2.2);
        doubleList.add(3.3);

        System.out.println("Sum of intList: " + sumOfList(intList));
        System.out.println("Sum of doubleList: " + sumOfList(doubleList));
    }
}

Output:

Sum of intList: 6.0
Sum of doubleList: 6.6

Lower Bounded Wildcards

Lower bounded wildcards restrict the unknown type to be a specific type or a supertype of that type. They are declared using the super keyword.

Syntax:

List<? super Integer> list = new ArrayList<>();

Example:

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

public class LowerBoundedWildcardExample {
    public static void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
    }

    public static void main(String[] args) {
        List<Number> numberList = new ArrayList<>();
        addNumbers(numberList);

        System.out.println("Number List: " + numberList);
    }
}

Output:

Number List: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

3. Use Cases for Wildcards

  • Method Parameters: Wildcards are commonly used in method parameters to allow the method to accept various types of lists.
  • Collections: Wildcards can be used with collections to write flexible and reusable code.
  • Generic Algorithms: Wildcards allow writing generic algorithms that can operate on different types.

4. Example: Using Wildcards

Example 1: Generic Print Method

Example:

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

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

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        stringList.add("Generics");

        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        printList(stringList);
        printList(intList);
    }
}

Output:

Hello
Generics
1
2

Example 2: Bounded Wildcards

Example:

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

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

    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(10);
        intList.add(20);

        List<Double> doubleList = new ArrayList<>();
        doubleList.add(1.5);
        doubleList.add(2.5);

        System.out.println("Sum of intList: " + sumOfNumbers(intList));
        System.out.println("Sum of doubleList: " + sumOfNumbers(doubleList));
    }
}

Output:

Sum of intList: 30.0
Sum of doubleList: 4.0

5. Guidelines for Using Wildcards

  • Use Unbounded Wildcards (?): When you want to accept any type and do not require any type-specific operations.
  • Use Upper Bounded Wildcards (<? extends T>): When you want to read items from a structure and need to perform operations on the elements that are guaranteed to be a subtype of a specific type.
  • Use Lower Bounded Wildcards (<? super T>): When you want to add items to a structure and need to ensure that the structure can accept elements of a specific type or its subtypes.

6. Conclusion

Wildcards in Java Generics provide a way to write more flexible and reusable code. They allow you to work with unknown types, making your code more adaptable to various situations. By understanding and using unbounded, upper bounded, and lower bounded wildcards, you can enhance the robustness and versatility of your Java programs.

Happy coding!

Comments