Friday 15 September 2017

What is Generics in Java with examples ? and What are the advantages of using Generics.

        In Java 1.5 added one important feature i.e Java Generics.  If you have been working on Collections and having java 5 or above versions then sure you have used it Generics.  At high level definition, generics are nothing but parameterized  types.
        It allows a class or method to operate on objects of various types while providing compile-time type safety. It adds compile-time type safety to the Collections framework and eliminates the need for casting.  This helps in detecting bugs at compile time itself.  Fixing compile time error is easier than run time errors .  It avoids the ClassCastException at run time.

Let's see an example, Writing a List without Generics as below,

   List list = new ArrayList(); 
   list.add(1); 
   list.add("A"); 
   Integer x = (Integer) list.get(1); //will throw ClassCastException at run time

In the above code, It will add any type of the Object into the list. It won't check the type safety hence there is no compile time error.  Only run time it can check for the type safety and will throw ClassCastException.

With Generics, same above code will be written as,
 
    List<Integer> list = new  ArrayList<Integer>(); 
    list.add(1); 
    list.add("A"); // It will show a compile time error.
    Integer x = list.get(0); 

The above code show compile time error while adding String to the list.  So it can type safety at compile time itself, it can not allow to add.  In this code explicit type casting is not required while getting the list values.

Let us see another advantage of Generics,  Type inference.

           Type inference is a Java compiler’s ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.
           Generic Methods introduce you to type inference, which enables you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets.


Generic Classes:-
 The Generic class is an ordinary class with type parameters, example as below

public class GenericClass<T> {
     private T t;
 
     public void add(T t) {
          this.t = t;
     }
 
     public T get() {
          return t;
     }
 
     public static void main(String[] args) {

           GenericClass<String> stringClass = new GenericClass<String>();
           GenericClass<Integer> integerClass = new GenericClass<Integer>();
      
           stringClass.add(new String("Hello World"));
           integerClass.add(new Integer(1));
           System.out.println("Integer Value: " + integerClass.get());
           System.out.println("String Value: " + stringClass.get());
     }
}

Output:
Integer Value: 1
String Value: Hello World


Wildcards in Generics:-
 In generic code, the question mark(?)  is called the wildcard, represents an unknown type.  There are two types of wildcards in Generics 1) Bounded and  2) Unbounded 
The bounded wildcard is further divided into upper bounded wildcard and lower bounded wildcard.

Upper Bounded Wildcards

      To declare an Upper bounded wildcard, use the wildcard character(?)  followed by the extends keyword, followed by its upper bound(type). Note that, in this context,  extends is used in a general sense to mean either extends (for classes) or implements (for interfaces).  Upper bounded wildcard restricts the unknown type to be a specific type or sub type of that type.
   
      Consider the below code,

public static void sumOfList(List<? extends Number> list) {
   //some code
}
 
 Here the argument list is bound to the Number type. This method works on lists of Number and the sub types of Number such as Integer, Double and Float.

Lower Bounded Wildcards

To declare a Lower bounded wildcard, use the wildcard character(?) , followed by the super keyword, followed by its lower bound(type). A lower bounded wildcards restricts the unknown type to be a specific or super type of that type.
      Consider the below code,


public static void sumOfList(List<? super Integer> list) {
   //some code

This method works on a lists of Integer or super type of Integer i.e lists of Number.


Unbounded Generics
     The Unbounded wildcard type is specified using the wildcard character(?) . e.g List<?> . This is called  a list of unknown type.  This is useful when you are writing a method that does only read only operation or uses only common methods defined in the interface and doesn't use any implementation specific methods.

      For example, you can write a common method that just displays the elements of the list or calculates the size of the list.
 
public void display(List<?> list){
     Iterator<?> itr = list.iterator();
     while (itr.hasNext()) {
          System.out.println(itr.next());
     }
}
 
public static int sizeOfList(List<?> list){
      return list.size();
}


Type Erasure :-

        Generics were introduced to provide tighter type checks at compile time and to support generic programming. Type erasure is nothing but the Java compiler replaces the type parameters with their bounded types or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. It also Insert type casts if necessary to preserve type safety.
        Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no run-time overhead.
        Consider the below class,'
 
 public class GenericClass<T> {
       private T t;
 
       public void add(T t) {
            this.t = t;
       }
 
       public T get() {
            return t;
       }
 }

Here T is an unbounded parameter so during type erasure process Java compiler replaces it with the Object. So the code after compilation is as follows,
 

  public class GenericClass {
       private Object t;
 
       public void add(Object t) {
            this.t = t;
       }
 
       public Object get() {
            return t;
       }
 }


Note : -  Standard convention to use Generic parameters are ,
         T -  Type parameter
         E -  Elements (using in LinkedList)
         K - Key in Map
         V - value in Map
         N - Number

1 comment: