Explain Wildcard Concept And Type of Wildcard in Java Generics

In Java Generics, a Wildcard is represented by a ? or Question mark that resembles Unknown Type which is used to resolve incompatibility issue. Further, Wildcard can be used in various places such as, in form of the type parameter, field, as  return type and local variable. It can’t be used to instantiate a generic class or invoke a generic method.

In short, a wildcard concept provides a way to cast a class type to its superclasses or subclasses.

Situation:

public class WildCardIssue {
    public static void main(String[] args){
        ArrayList<String> arrayList = new ArrayList<String>();
        WildCardIssue obj = new WildCardIssue();
        obj.print(arrayList);
    }
    
    void print(ArrayList<String> obj){
        // Doing some Operations
        obj.add("Hi");
        obj.add("This is Programmerbay");
        obj.add("Wildcard");
        System.out.println(obj);

    }
    
}

Explanation:

The method is capable of handling ArrayList of String type only and can be invoked for String ArrayList. Further, operations within that method can be performed which is compatible with the respective passed parameter type. In this case, its support limits to String Object.

Suppose, if requirement changes, the method would no longer be useful. Instead, to overcome such situation, we can use wildcard argument or ? to provide support for other types also.

Wildcards can be classified into three types, Upper Bounded wildcard, Lower Bounded Wildcard, and Unbounded Wildcard.

Type of Wildcards in Java Generics :

Unbounded Wildcard:

An unbounded wildcard is specified with <?> which provides the capability to support for unknown types. It is suitable use when the behaviour of a method is not bounded to specific type and also not dependent on Type Parameter.

void print (ArrayList<?> obj) :  With the use of Unbounded Wildcard, a method becomes capable of accepting any unknown type and provides the advantage of code reusability and reduce the code size. The method can be invoked for any type as its param comes with Wildcard or ? argument , whether it is string,integer,double or any other.

The below program provides flexibility to support various type of object, but throw compile time error while performing add operation on a list.

public class WildCardIssue {
    public static void main(String[] args){
        ArrayList<String> arrayList = new ArrayList<String>();
        WildCardIssue obj = new WildCardIssue();
        obj.print(arrayList);

        ArrayList<Integer> integerArrayList = new ArrayList<Integer>();
        obj.print(integerArrayList);
    }

    void print(ArrayList<?> obj){
        obj.add("Hi");   // Compile time error,
        obj.add("This is Programmerbay");
        obj.add("Wildcard");
        System.out.println(obj);

    }

}

In case of List, only “null” can be added as null is member of any type. Therefore, wildcards are generally used for non-manipulation operations only.

The below example would work fine:

public class WildCardIssue {
    public static void main(String[] args){
        ArrayList<String> arrayList = new ArrayList<String>();
        WildCardIssue obj = new WildCardIssue();

        // Doing some operation
        arrayList.add("Hi");
        arrayList.add("This is Programmerbay");
        arrayList.add("Wildcard");
        obj.print(arrayList);

        ArrayList<Integer> integerArrayList = new ArrayList<Integer>();
        integerArrayList.add(5);
        integerArrayList.add(10);
        integerArrayList.add(20);
        obj.print(integerArrayList);
    }

    void print(ArrayList<?> obj){
        System.out.println(obj);

    }

}

Output:

[Hi, This is Programmerbay, Wildcard]
[5, 10, 20]

Upper Bounded Wildcard:

An Upper Bounded Wildcard is expressed by using <? extends anyClassName or anyInterfaceName>. It restricts the acceptable types to the extended className object or its subclass. For example, extending Number class would restrict the passed parameter type to Number Object or it subclasses such as Integer, Double and more.

void print (ArrayList<? extends anyClass or anyInterface> obj) : In Upper Bounded wildcard, the ? or question mark always extends a class or an interface. The method can be invoked when passed param type is either the passed class type or its child classes, in case of interface, either the interfaces or implementation classes. It also triggers compile time error while performing add operation in arraylist.

The below ‘print’ method supports only Number or it subclasses such as Double,Integer.

public class UpperBoundedWildCardExample {
    public static void main(String[] args){
        UpperBoundedWildCardExample obj = new UpperBoundedWildCardExample();

        ArrayList<Integer> integerArrayList = new ArrayList<Integer>();
        // Doing some operations

        integerArrayList.add(5);
        integerArrayList.add(10);
        integerArrayList.add(20);
        obj.print(integerArrayList);
    }

    void print(ArrayList<? extends Number> arrayList){
        for (Number singleElement:
             arrayList) {
            System.out.println(singleElement);
        }

    }

}

Lower Bounded Wildcard:

A Lower Bounded Wildcard is specified by using <? super anyClassName or anyInterfaceName>. It restricts the acceptable types to the passed className object or its superclasses (ancestors) only. For example, passing Integer would restrict the passed parameter type to Integer Object or its superclasses such as Number.

void print (ArrayList<? super anyClass or anyInterface> obj) : In Lower bounded wildcard, ? is used to set lower bound which is not allowed at class level with T type parameter. But, at method level Super keyword is allowed . The method can be invoked when passed param type is either class type or its parent classes.

The className type is allowed to perform add operation in Lower Bounded Wildcard

public class LowerBoundedExample {
    public static void main(String[] args) {
        LowerBoundedExample obj = new LowerBoundedExample();

        ArrayList<String> stringArrayList = new ArrayList<String>();
        obj.print(stringArrayList);
    }

    void print(ArrayList<? super String> arrayList) {
        arrayList.add("Hi");
        arrayList.add("String is allowed in Lower bound");
        arrayList.add(null);
        System.out.println(arrayList);

    }
}

 

As compared to previously discussed wildcards types, a lower bounded wildcard provides the flexibility to add class object passed in type argument to an ArrayList. For instance, <? super Integer> would allow adding elements of Integer type to an arraylist.

Leave a Reply