Saturday, January 20, 2007

Java Generics basics

Generics make sure the classes are type safe at compile time when those classes are working with arbitrary objects. See the following example.

import java.util.ArrayList;
import java.util.List;

/**
* @author pkannan
*
*/
public class FirstExample
{
/**
* @param args
*/
public static void main(String[] args)
{
List testList = new ArrayList();
testList.add(new Integer(6));

String string = (String)testList.get(0);
}
}

The above class compiles perfectly, but at runtime... Bang! Throws exception

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer
at com.learner.generics.FirstExample.main(FirstExample.java:24)

If we could caught this exception at compile time our application would be very safe at runtime. Here come generics to do that for us. I’ll change some of the code from the above class with the help of generics.

List<Integer> testList = new ArrayList<Integer> ();
testList.add(new Integer(6));

String string = (String)testList.get(0); //--------------1

When I write the code (1), the compiler would tell me; hey” can not cast from Integer to String”. So I can be more careful when I write code and I will be safe. Thanks to generics!


Generics are mostly used with Collections. Collections deals with objects which are java.lang.Object type. When we save any object to Collections it saves as java.lang.Object type and it returns as such. So a casting is needed when we get it out. Generics solve this issue for us. See the following example. I use the same code bloc that I used previously.

List<Integer> testList = new ArrayList<Integer>();
testList.add(new Integer(6));

Integer intValue = testList.get(0);

Wow! How simple. Now we’ll look into the generic types and how to define generic classes.

GenericTypes
Parameterized Types (Classes and Interfaces)

When a class is defined with the parameter (or parameters) then it’s called as Parameterized class. It’s another name for generic class. The parameter is called as Type parameter.When I refer generic class it applies for generic Interface too.

Class CheckArrayList<Type> extends ArrayList()
{
// implementation goes here.
}

In then above code, CheckArrayList is parameterized with one parameter type ‘Type’ but it can have more than one parameter. Type parameter is an unqualified identifier that is used as a place holder .This will be replaced by the compiler with the specified Type when an object is created for the generic class. See the following example

CheckArrayList <Integer> testList = new CheckArrayList <Integer> ();

-----------------------------------------------------------------
package com.learner.generics;

public class SecondExample<t>
{

public static void main(String[] args)
{
SecondExample<Integer> example = new SecondExample<Integer>();
}
}

The Type parameter is replaced with Integer.

As we know ArrayList is the direct super type of the CheckArrayList as it is extended from ArrayList. If a class does not extend from any specific class then that class’s super type is java.lang.Object.This rules apply for Type variables too. See the following example

Class MyException<Type> extends Exception
{
//implementation goes here
}

In the above code, String is the super type for ‘Type” Type parameter. It it’s not extended from any specific class then that type parameter’s super type is java.lang.Object.


When it comes in hierarchies of generic classes, one rule applies. A class or Type parameter can not have two different parameterization of the same class or interface at the same time. See the following example.

interface MyInterface<T>
{

}

class Second<Type> implements MyInterface<Integer>
{

}

class Third<Type> extends Second<Integer> implements MyInterface<String>
{
}

The compiler will give you an error you saying “The interface MyInterface cannot be implemented more than once with different arguments “.


Raw Types and Type erasure

Raw types are the one which is a parameterized type removed of its parameters. .When a generic type is instantiated, the compiler translates those types by a technique called 'type erasure' — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Raw types are used for legacy codes that use non generic codes. See the following code

List<Integer> genericList = new ArrayList<Integer>();
List rawList = new ArrayList(); // Raw Types
genericList.add(new Integer(6));

rawList = genericList; // Perfectly fine
genericList = rawList; // here the compiler will warn you.

The compiler will warn you as ‘Type safety: The expression of type List needs unchecked conversion to conform to List

Generics and Methods

Generics can be used in methods too. The ankle brackets will be placed after all the modifiers of the Class and right before the return type. See the following example

public <E> get(int index) {
rangeCheck(index);

return elementData[index];
}

The syntax is same as generic class. Two methods can not have same name and argument types if they have then the compiler will give an error.

Generics and Arrays

You can not create a array of generic type and arrays of type variable. The following code will generate a compile time error

Vector<Integer> vector [] = new Vector<Integer>[10];


Generics and Exceptions

Generics can be used for exception in throws clause. It can not be used in catch clauses. See the following example.

package com.learner.generics;

import java.io.IOException;

public class SecondExample<V>
{
public void sample() throws V
{
//Implementation goes here.
}

public static void main(String[] args)
{

SecondExample<IOException> example = new SecondExample<IOException>();
try
{
example.sample();
}
catch (IOException e)
{
e.printStackTrace();
}
}

}

0 comments: