Tuesday, November 17, 2015

Java Generics

Generics was added in Java 5 to provide compile-time type checking and removing risk of ClassCastException that was common while working with collection classes. The whole collection framework was re-written to use generics for type-safety. Let’s see how generics help us using collection classes safely.

In the heart of generics is “type safety“. What exactly is type safety? It’s just a guarantee by compiler that if correct Types are used in correct places then there should not be any ClassCastException in runtime. A usecase can be list of Integer i.e. List. If you declare a list in java like List, then java guarantees that it will detect and report you any attempt to insert any non-integer type into above list.
Another important term in java generics is “type erasure“. It essentially means that all the extra information added using generics into sourcecode will be removed from bytecode generated from it. Inside bytecode, it will be old java syntax which you will get if you don’t use generics at all. This necessarily helps in generating and executing code written prior java 5 when generics were not added in language.

Let’s understand with an example.
List list = new ArrayList();
 
list.add(1000);     //works fine
 
list.add("mukesh"); //compile time error;
When you write above code and compile it, you will get below error: “The method add(Integer) in the type List is not applicable for the arguments (String)“. Compiler warned you. This exactly is generics sole purpose i.e. Type Safety.

Second part is getting byte code after removing second line from above example. If you compare the bytecode of above example with/without generics, then there will not be any difference. Clearly compiler removed all generics information. So, above code is very much similar to below code without generics.
List list = new ArrayList();
 
list.add(1000);  


In generic code, the question mark (?), called the wildcard, represents an unknown type. A wildcard parameterized type is an instantiation of a generic type where at least one type argument is a wildcard. Examples of wildcard parameterized types are Collection, List, Comparator and Pair. 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.
Having wild cards at difference places have different meanings as well. e.g.
  • Collection denotes all instantiations of the Collection interface regardless of the type argument.
  • List denotes all list types where the element type is a subtype of Number.
  • Comparator denotes all instantiations of the Comparator interface for type argument types that are supertypes of String.
A wildcard parameterized type is not a concrete type that could appear in a new expression. It just hints the rule enforced by java generics that which types are valid in any particular scenario where wild cards have been used.
For example, below are valid declarations involving wild cards:

Collection coll = new ArrayList();
//OR
Listextends Number> list = new ArrayList();
//OR
Pair pair = new Pair();
And below are not valid uses of wildcards, and they will give compile time error.
Listextends Number> list = new ArrayList();  //String is not subclass of Number; so error
//OR
Comparatorsuper String> cmp = new RuleBasedCollator(new Integer(100)); //String is not superclass of Integer
Wildcards in generics can be unbounded as well as bounded. Let’s identify the difference in various terms.

Unbounded wildcard parameterized type

A generic type where all type arguments are the unbounded wildcard “?” without any restriction on type variables. e.g.
ArrayList  list = new ArrayList(); 
//or
ArrayList  list = new ArrayList(); 
//or
ArrayList  list = new ArrayList(); 

Bounded wildcard parameterized type

Bounded wildcards put some restrictions over possible types, you can use to instantiate a parametrized type. This restriction is enforced using keywords “super” and “extends”. To differentiate more clearly, let’s devide them into upper bounded wildcards and lower bounded wildcards.

Upper bounded wildcards
For example, say you want to write a method that works on List, List, and List; you can achieve this by using an upper bounded wildcard e.g. you would specify List. Here Integer, Double are subtypes of Number class. In layman’s terms, if you want generic expression to accept all subclasses of a particular type, you will use upper bound wildcard using “extends” keyword.
public class GenericsExample
{
   public static void main(String[] args)
   {
      //List of Integers
      List ints = Arrays.asList(1,2,3,4,5);
      System.out.println(sum(ints));
       
      //List of Doubles
      List doubles = Arrays.asList(1.5d,2d,3d);
      System.out.println(sum(doubles));
       
      List strings = Arrays.asList("1","2");
      //This will give compilation error as :: The method sum(List) in the
      //type GenericsExample is not applicable for the arguments (List)
      System.out.println(sum(strings));
       
   }
    
   //Method will accept
   private static Number sum (Listextends Number> numbers){
      double s = 0.0;
      for (Number n : numbers)
         s += n.doubleValue();
      return s;
   }
}

Lower bounded wildcards
If you want a generic expression to accept all types which are “super” type of a particular type OR parent class of a particular class then you will use lower bound wildcard for this purpose, using ‘super’ keyword.
In below given example, I have created three classes i.e. SuperClass, ChildClass and GrandChildClass. There relationship is shown in code below. Now, we have to create a method which somehow get a GrandChildClass information (e.g. from DB) and create an instance of it. And we want to store this new GrandChildClass in an already existing list of GrandChildClasses.
Here problem is that GrandChildClass is subtype of ChildClass and SuperClass as well. So any generic list of SuperClasses and ChildClasses is capable of holding GrandChildClasses as well. Here we must take help of lower bound wildcard using ‘super‘ keyword.
package test.core;
 
import java.util.ArrayList;
import java.util.List;
 
public class GenericsExample
{
   public static void main(String[] args)
   {
      //List of grand children
      List grandChildren = new ArrayList();
      grandChildren.add(new GrandChildClass());
      addGrandChildren(grandChildren);
       
      //List of grand childs
      List childs = new ArrayList();
      childs.add(new GrandChildClass());
      addGrandChildren(childs);
       
      //List of grand supers
      List supers = new ArrayList();
      supers.add(new GrandChildClass());
      addGrandChildren(supers);
   }
    
   public static void addGrandChildren(Listsuper GrandChildClass> grandChildren)
   {
      grandChildren.add(new GrandChildClass());
      System.out.println(grandChildren);
   }
}
 
class SuperClass{
    
}
class ChildClass extends SuperClass{
    
}
class GrandChildClass extends ChildClass{
    
}

What is not allowed to do with Generics?

a) You can’t have static field of type
b) You can not create an instance of T
c) Generics are not compatible with primitives in declarations
d) You can’t create Generic exception class
,

No comments:

Post a Comment