Wednesday, August 04, 2010

Java Generics - Part 1

Generics were introduced in JDK 5.0. Generics help write re-usable code without losing type safety. Type safety helps write robust programs. The most common usage of generics is in the collections API.

 

Prior to generics, a Collection could theoretically hold values of various types. Like so:

 

import java.util.Collection;

import java.util.LinkedList;

class Generics1

{

       public static void main(String[] args)

       {

              Collection nameList = new LinkedList();              

              nameList.add( new Integer(0) ); // 1 Integer added to collection

              nameList.add( "test" );         // 2 String added to collection

              System.out.println( nameList );

       }

}

 

Compiling the above code with JDK 5.0 and above will result in a “warning”. But the class file does get generated. Like so:

 

$ javac Generics1.java

Note: Generics1.java uses unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

 

Running the class file does give the following output:

 

$ java Generics1

[0, test]

 

But now we have a Collection with a Integer and a String. Its highly un-likely that a programmer would expect to see a Integer in a nameList.

 

To enforce type safety that the nameList can only hold String instances, change the declaration of nameList like so:

 

import java.util.Collection;

import java.util.LinkedList;

class Generics1

{

       public static void main(String[] args)

       {

              Collection<String> nameList = new LinkedList<String>();

              nameList.add( new Integer(0) ); // Violation (Integer added to String collection) caught by compiler.

              nameList.add( "test" );        

              System.out.println( nameList );

       }

}

 

 

Now, the compiler checks each operation on the Collection and ensures that only Strings find place in the Collection (type correctness). Compilation fails if there are violations (like above):

 

$ javac Generics1.java

Generics1.java:8: add(java.lang.String) in java.util.Collection<java.lang.String> cannot be applied to (java.lang.Integer)

                nameList.add( new Integer(0) );

                        ^

1 error

 

Since all Collection elements are held as java.lang.Object in a non-generic Collection, all fetches must be cast to the appropriate type. Failing to do so will result in a compilation failure. Like so:

 

import java.util.List;

import java.util.LinkedList;

class Generics1

{

       public static void main(String[] args)

       {

              List nameList = new LinkedList();

              nameList.add( "test" );

              String firstName = nameList.get(0);

System.out.println( firstName );

       }

}

 

$ javac Generics1.java

Generics1.java:9: incompatible types

found   : java.lang.Object

required: java.lang.String

                String firstName = nameList.get(0);

                                               ^

Note: Generics1.java uses unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

1 error

 

This issue can be resolved by explicitly casting the assignment or typing the collection with generics like so:

 

              List<String> nameList = new LinkedList<String>();

 

$ java Generics1

Test

 

In the above case, List is a generic interface with a type parameter.

No comments: