The best programs are written so that computing machines can perform them quickly and so that human beings can understand them clearly. A programmer is ideally an essayist who works with traditional aesthetic and literary forms as
well as mathematical concepts, to communicate the way that an algorithm works and to convince a reader that the results will be correct. Donald E. Knuth

Generics

Basics of Generics
Generics is a feature which was introduced in Java 5 to be used with Collection classes. If you have compiled the programs in the previous module, you would have got message indicating use of deprecated feature and that is due to the absence of Generic feature. This is a very simple concept and following programs will make it clear.

Generic feature not used ( deprecated )
import java.util.*;
public class NonGenericDemo {
   public static void main(String args[]) {
      ArrayList alist = new ArrayList(); // create an array list

      /* add elements in the ArrayList */
      alist.add("John"); // a string element is added
      alist.add(5); // an integer element added
      alist.add('c'); // a character element added
      alist.add(13.67f); // a float element added

      System.out.print("ArrayList elements : ");
      Iterator it = alist.iterator();
      while(it.hasNext()) {
         System.out.print(it.next() + " ");
      }
      System.out.println();
   }
}

Generic feature used ( modern programming technique )
import java.util.*;
public class GenericDemo {
   public static void main(String args[]) {
     // create an array list to store string elements only
      ArrayList<String> alist = new ArrayList<String>();

      /* add elements in the ArrayList */
      alist.add("Rahul");
      alist.add("Tina");
      alist.add("Anjali");
      alist.add("Aman");
      // alist.add(2);  /* will lead to compilation error */

      System.out.print("ArrayList elements : ");
      Iterator it = alist.iterator();
      while(it.hasNext()) {
         System.out.print(it.next() + " ");
      }
      System.out.println();
   }
}

In the first program, the arrayList could store any type of object. This could lead to ClassCastException at runtime if objects are not typecasted properly while accessing them. As we can see in the second program, use of Generic forces the storage of specific type of objects in a data structure. This ensures compile-time type checking and avoids any typecasting. The best programming strategy is to handle most of the problems at compile time. Let us see another example which differentiates between generic and non-generic programs.

Generic feature not used ( deprecated )
import java.util.*;
public class NonGenericListDemo {
   public static void main(String args[]) {
      List al = new ArrayList();
      al.add("Anjali");
      al.add("Rahul");
      al.add("Tina");
      // al.add(3.56); // valid statement but will result into
                       // runtime exception in the 'for' loop

      for(Object ob : al) {
          String str = (String)ob; //type casting required
          System.out.println(str);
      }
   }
}

Generic feature used ( modern programming technique )
import java.util.*;
public class GenericListDemo {
   public static void main(String args[]) {
      List<String> al = new ArrayList<String>();
      al.add("Anjali");
      al.add("Rahul");
      al.add("Tina");
      // al.add(3.67); /* compile time error */

      for(String ob : al) {
          String str = ob;  // no typecasting required
          System.out.println(str);
      }
   }
}


Generics usage in Map
Following program demonstrates how generic feature can be used with Map :

import java.util.*;
public class GenericMapDemo {
   public static void main(String args[]) {
      HashMap<Integer, String> hm = new HashMap<Integer, String>();
      /* add key-value pairs in the HashMap where
       * key -> Roll No. and value -> Name
       */
      hm.put(1, "Anjali");
      hm.put(2, "Rahul");
      hm.put(3, "Tina");

      /* find the student with roll no. 2 */
      int roll = 2;
      String stud = hm.get(roll); // no typecasting required
      System.out.println("Student with roll no " + roll + " is " + stud);

      /* find all the students and their roll no. */
      System.out.println("All Students data :- ");
      Set<Map.Entry<Integer,String>> s = hm.entrySet();
      Iterator<Map.Entry<Integer,String>> it = s.iterator();
      while(it.hasNext()) {
         Map.Entry m = it.next();
         System.out.println(m.getKey() + " " + m.getValue());
      }
   }
}


Generic Methods
A generic method can operate on any data type i.e we can write one generic method which can be called with arguments if different types. In the following program, we write a generic display( ) method to print array elements.

import java.util.*;
public class GenericMethodDemo {
   static <T> void display(T arr[]) {
      System.out.println("Array Elements :- ");
      for(T ele : arr) {
         System.out.print(ele + " ");
      }
      System.out.println();
   }
   public static void main(String args[]) {
      Integer arr1[] = { 5, 7, 2, 9, 6 };
      String arr2[] = { "Rahul", "Anjali", "Tina" };
      Character arr3[] = { 'c', 'v', 'u', 't' };
      display(arr1);
      display(arr2);
      display(arr3);
   }
}


Generic method to get max and min of comparable objects

import java.util.*;
class Relational {
   // Get the maximum of two objects 	
   <T extends Comparable<T>> T max(T x, T y) {
      if (x.compareTo(y) > 0)
         return x;
       else
         return y;
   }
   // Get the minimum of two objects
   <T extends Comparable<T>> T min(T x, T y) {
      if (y.compareTo(x) > 0)
          return x;
      else
          return y;
   }
}

public class GenericRelationalDemo {
   public static void main(String args[]) {
      Relational r1 = new Relational();
      int max_val = r1.max(7, 9);
      double min_val = r1.min(2.67, 2.68);
      System.out.println("max_val : " + max_val);
      System.out.println("min_val : " + min_val);
   }
}


Generic Classes
All classes and interfaces in the Collection framework are generic and we have already used those classes previously. Now we will see how to make user defined classes generic. See the program below :

import java.util.*;

/* Define a generic */
class GenDisplay<T> {
   T x, y;
   GenDisplay(T var1, T var2) {
      x = var1;
      y = var2;
   }
   void display() {
      System.out.println(x + "  " + y);
   }
}

public class GenericClassDemo {
   public static void main(String args[]) {
      GenDisplay<Integer> r1 = new GenDisplay<Integer>(7, 9);
      GenDisplay<Double> r2 = new GenDisplay<Double>(2.67, 2.68);
      GenDisplay<String> r3 = new GenDisplay<String>("Rahul", "Anjali");
      r1.display();
      r2.display();
      r3.display();
   }
}


Autoboxing and Unboxing
Autoboxing is a feature of Java language which is used in conjunction with generics to automatically convert primitive data type to their corresponding Wrapper Classes. For e.g conversion of int data type to Integer class or conversion of char data type to Character class. Generic classes cannot deal with primitive data types. So, this conversion is important and it is done automatically and we have already used this feature in lot of programs previously. The reverse operation ( conversion from Wrapper Class objects to primitive data types ) is known as Unboxing. As a programmer, we don't have to worry about how boxing and unboxing is handled by Java Compiler internally. See the program below :

import java.util.*;
public class AutoBoxingDemo {
   public static void main(String args[]) {
      // TreeSet can hold Integer objects only
      TreeSet<Integer> ts = new TreeSet<Integer>();
      int x = 7; // x is of 'int' ( primitive ) data type
      ts.add(x); // AutoBoxing ( 'int' to 'Integer' conversion )
      ts.add(5); // AutoBoxing
      Iterator it = ts.iterator();
      while(it.hasNext()) {
         x = (Integer)it.next(); // UnBoxing
         System.out.println(x);
      }
   }
}

Back | Next