Generics wildcards Tutorial with examples

Java Generic's wildcards is a mechanism in Java Generics aimed at making it possible to cast a collection of a certain class.

Generics Wildcards

In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.

Generics Upper Bounded Wildcards

You can use an upper bounded wildcard to relax the restrictions on a variable. For example, say you want to write a method that works on List<Integer>, List<Double>, and List<Number>; you can achieve this by using an upper bounded wildcard.

To declare an upper-bounded wildcard, use the wildcard character ('?'), followed by the extends keyword, followed by its upper bound. Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

To write the method that works on lists of Number and the subtypes of Number, such as Integer, Double, and Float, you would specify List<? extends Number>. The term List<Number> is more restrictive than List<? extends Number> because the former matches a list of type Number only, whereas the latter matches a list of type Number or any of its subclasses.

Consider the following process method:
public static void process(List<? extends Foo> list) { /* ... 

*/ }


The upper bounded wildcard, <? extends Foo>, where Foo is any type, matches Foo and any subtype of Foo. The process method can access the list elements as type Foo:
public static void process(List<? extends Foo> list) {
    for (Foo elem : list) {
        // ...
    }
}


In the foreach clause, the elem variable iterates over each element in the list. Any method defined in the Foo class can now be used on elem.
The sumOfList method returns the sum of the numbers in a list:
public static double sumOfList(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}


The following code, using a list of Integer objects, prints sum = 6.0:
List<Integer> li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));
A list of Double values can use the same sumOfList method. The following code prints sum = 

7.0:
List<Double> ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));

Generics Unbounded Wildcard

Sometimes we have a situation where we want our generic method to be working with all types, in this case unbounded wildcard can be used. Its same as using <? extends Object>.

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


We can provide List<String> or List<Integer> or any other type of Object list argument to the printData method. Similar to upper bound list, we are not allowed to add anything to the list.

Generics Lower bounded Wildcard

Suppose we want to add Integers to a list of integers in a method, we can keep the argument type as List<Integer> but it will be tied up with Integers whereas List<Number> and List<Object> can also hold integers, so we can use lower bound wildcard to achieve this. We use generics wildcard (?) with super keyword and lower bound class to achieve this.

We can pass lower bound or any super type of lower bound as an argument in this case, java compiler allows to add lower bound object types to the list.
public static void addIntegers(List<? super Integer> list){    

     
 list.add(new Integer(50));     
} 

Generics wildcards and Subtyping

Given the following two regular (non-generic) classes:
class A { /* ... */ }
class B extends A { /* ... */ }


It would be reasonable to write the following code:
B b = new B();
A a = b;


This example shows that inheritance of regular classes follows this rule of subtyping: class B is a subtype of class A if B extends A. This rule does not apply to generic types:
List<B> lb = new ArrayList<>();
List<A> la = lb;   // compile-time error


Given that Integer is a subtype of Number, what is the relationship between List<Integer> and List<Number>?

Although Integer is a subtype of Number, List<Integer> is not a subtype of List<Number> and, in fact, these two types are not related. The common parent of List<Number> and List<Integer> is List<?>.

In order to create a relationship between these classes so that the code can access Number's methods through List<Integer>'s elements, use an upper bounded wildcard:
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a 

subtype of List<? extends Number>


Because Integer is a subtype of Number, and numList is a list of Number objects, a relationship now exists between intList (a list of Integer objects) and numList. The following diagram shows the relationships between several List classes declared with both upper and lower bounded wildcards.

Type Erasure

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  • Insert type casts if necessary to preserve type safety.
  • Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

For example if we have a generic class like below;
public class Test<T extends Comparable<T>> {       
 private T data;     
 private Test<T> next;       
 public Test(T d, Test<T> n) {         
  this.data = d;         this.next = n;     
  }       
 public T getData() { 
  return this.data; 
 } 
}


The Java compiler replaces the bounded type parameter T with the first bound interface, Comparable, as below code:
public class Test {       
 private Comparable data;     
 private Test next;       
 public Node(Comparable d, Test n) {         
  this.data = d;         
  this.next = n;     
 }       
 public Comparable getData() { 
  return data; 
 } 
} 


MUST READ : Java Generics Tutorial with Examples.

Generics wildcards Tutorial with examples

Hope we are able to explain you basic details of Java Generics, if you have any questions or suggestions please write to us using contact us form.(Second Menu from top left).

Please share us on social media if you like the tutorial.
SHARE
    Blogger Comment
    Facebook Comment