Tuesday 4 March 2014

Generics Introduction

Generics

Introduced in Java 5.0

Generics enable classes and interfaces to be parameters when defining classes, interfaces and methods. Classes and interfaces acts define "type" parameters.

These "type parameters"(classes and Interfaces as parameters) are different from "formal parameters" which are used in the method declaration in the sense that inputs to formal parameters used for method declaration are values whereas inputs to types parameter are types (Classes/Interfaces).


Advantages of using Generics :--

1. Stronger type checks at compile time.

2. Elimination of casts. For example :--
 
   Before Java 1.5 without generics, following code snippet requires a cast

        List list = new ArrayList();
    list.add("hello");
    String s = (String) list.get(0);

  With introduction of generics in Java verison 1.5, code doesn't require casting :--
        
    List<String> list = new ArrayList<String>();
    list.add("hello");
    String s = list.get(0);   // no cast

3. Enabling programmers to implement generic algorithms.
   By using generics, programmers can implement generic algorithms that work on collections of different  types, can be customized, and are type safe and easier to read.


Generic Types (Generic classes and Interfaces):--

A generic type is a Class or Interface that is parametrized over types.

A simple Class Person, without generics looks like this :--

public class Person
{

   private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
   
}

Since Person class methods accepts or return an object, you are free to pass whatever you want, provided it's not a primitive. There is no way to verify at compile time how class is being used.


A generic class is defined with the following format :--

class class-name <T1, T2, T3 .......Tn>
{

}


The type parameter section delimited by angel brackets(<>) specifies the type parameter or type variables.

Generic version of Person class used above looks like this :--

//<T> repe
public class Person <T>
{

  private T t;

  public void set(T t) { this.t = t; }
  public T get() { return t; }

  public static void main(String[] args)
  {
      Person<String> p1 =  new Person<String>();
      p1.set("Abc");
      System.out.println(p1.get());
   
      Person<Integer> p2 =  new Person<Integer>();
      p2.set(1);
      System.out.println(p2.get());
     
  }
}


Here are the output of the above example :--

Abc
1

Here T specifies type variable. A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.

In above example, we are creating two instances of Person class. Instance p1 uses String type where as p2 uses as Integer type.Hence type parameters provide a way for you to re-use the same code with different inputs types.

  
Generic Methods :--

As we can have parametrized class, we can also define a parametrized type at a more granular level—a method level.Imagine you want to create a method that takes an instance of any type,instantiates an ArrayList of that type, and adds the instance to the ArrayList. The class itself doesn't need to be generic; basically we just want a utility method that we can pass a type to and that can use that type to construct a type safe collection. Using a generic method, we can declare the method without a specific type and then get the type information based on the type of the object passed to the method. For example:

import java.util.*;
public class CreateAnArrayList
{
            public <U> void makeArrayList(U u)
           {

               // take an object of an  unknown type and use a "U" to represent the type
                List<U> list = new ArrayList<U>(); // now we can create the  list using "U"
               list.add(u);
         }
}

The strangest thing about generic methods is that you must declare the type variable BEFORE the return type of the method.

Above Person class is modified to add a generic method named displayData(), which does nothing but displays whatever data is passed to that method.

//<T> defines the parameterize type
public class Person <T>
{

  private T t;

  public void set(T t)
  {
      this.t = t;
  }
 
  public T get()
  {
      return t;
  }
 
 //Generic method.  
//In generic method, the parametrized type is declared before return type of the method    
//Here U defines the  parametrized type which can be anything  
public <U> void displayData(U u)
  {
      System.out.println(u);
  }

  public static void main(String[] args)
  {
      Person<String> p1 =  new Person<String>();
      p1.set("Abc");
      System.out.println(p1.get());
   
      Person<Integer> p2 =  new Person<Integer>();
      p2.set(1);
      System.out.println(p2.get());
     
    //Invoking generic method named displayData with String type argument
     p1.displayData("String");
     
     //Invoking generic method named displayData with Integer type argument  
   p2.displayData(2);
  }
}


Here is output on execution :--

Abc
1
String
2

Bounded type parameters:--

There might be times when you want to restrict the types that can be used as type arguments in a parametrized type. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses or if a method that operates only on Strings might want to accept only String. Thus bounded type parameters restricts the arguments used in parameterized type.

For this "extends" keyword is used, followed by upper bound.

Above Person class is modified to accept bounded parametrized types.

//<T> defines the parameterize type
public class Person <T>
{

  private T t;

  public void set(T t)
  {
      this.t = t;
  }
 
  public T get()
  {
      return t;
  }
 
  //Generic method.
  //In generic method, the parameterized type is declared before return type of the method    
  public <U> void displayData(U u)
  {
      System.out.println(u);
  }
 
  //Bounded parametrized type. U is bounded to accept only String
  public <U extends String> void displayName(U u)
  {
      System.out.println("Name is :"+u);
  }

  //Bounded parametrized type. U is bounded to accept Numbers
  public <U extends Number> void displayAge(U u)
  {
      System.out.println("Age of the person is :"+u);
  }
 
  public static void main(String[] args)
  {
      Person<String> p1 =  new Person<String>();
      p1.set("Abc");
      System.out.println(p1.get());
   
      Person<Integer> p2 =  new Person<Integer>();
      p2.set(1);
      System.out.println(p2.get());
     
     
      //Invoking generic methods with different types
      p1.displayData("String");
      p2.displayData(2);
     
      //Invoking bounded parametrized type methods
      p1.displayAge(22);
      p1.displayName("Name");
  }
}

If you try to invoke diaplayAge() method which is bounded to accept only Numbers with String type, it will result in to compile time error. Similar is the case with  displayName() method, which is bounded to accepts only Strings.










    
     

   

    

No comments:

Post a Comment