Monday, October 27, 2008

Generics -Intermediary

Using Generic code with different Collection types

Lists

The usage with List Collections is quite straightforward as most of the java developers are familier with its API & its methods. Example of a code involving List interface is as follows:




List myList=new ArrayList();

MyObject a=new MyObject();

myList.add(a);

...

...




//Use this code to iterate

Iterator iterator=myList.Iterator();

while(iterator.hasNext()){

MyObject temp=iterator.next();

System.out.print(""+temp.toString());

}

//Or use the foll code

for(MyObject ss:myList){

MyObject temp=iterator.next();

System.out.print(""+temp.toString());

}

..

...

Sets

Set s=new HashSet();

System.out.println("Added an element"+s.add(new MyObject()));

s.add(new MyObject());

....




for(MyObject st:s)

System.out.println(st.toString());




Using TreeSets

Set mytreeSet=new TreeSet();




Now, class Power must be comparable, i.e. You can compare its elements .Eg. You can compare 1 & 2, "A" & "B", but how will you compare boy("Sumit") & boy("Rahul") ?

In order to do this, use java.util.Comparator interface.




Class Power implements Comparable{

public int FactorValue;

public int OtherValue;

.... other code including constructors

.

..

public int compareTo(Power t){ //Overriding the method through programmer implementation

if(this.FactorValue==t.FactorValue)

return 0;

if(this.FactorValue
return -1;

if(this.FactorValue>t.FactorValue)

return 1;

else

return 0;//Maybe throw an Assertion Error or an exception

}

//OR, You Could Also Use The Following Override


public int compareTo(Power t){ //Overriding through delegation

return(this.FactorValue.compareTo(t.FactorValue()));

/*

Works because Comparison is delegated to the int variable FactorValue in our class;

*/

}

}




If you wish to sort your object in a different manner, then use a Comparator. You can use it to sort even those class that you cannot modify. In such a case, create a (3rd Party) class like:




class OtherSort implements Comparator{

public int compare(MyObjectme,MyObject you){

return(me.OtherValue.compareTo(you.OtherValue));

}

}

Maps

Any class you use in a Map must implement the hashCode() and equals() methods. If you don't do so, then no compile/run time errors will occur, the code will just would'nt function the way it was supposed to.

Usage:

Map myMap=new HashMap();

Type1=Type Of KEY

Type2=Type Of VALUE

eg. Map shopList=new HashMap();




class Human{

String name;

Human(String s){

name=s;

}

public boolean equals(Object o){

if((o instanceof Human)&& ((Human)o).name==name)

return true;

else

return false;

}

public int hashCode(){ //More unique the hashCode returns a value,

return 5; //More efficient your class will be in retrieveing tho object from Map

}

public static void main(String[] args){

Map map=new HashMap();

map.put("we",new Human("Sumit"));

...

...

Human us;

us=map.get("we");

System.out.print(""+us.name);

}

}

Also, this is a valid code segment

Map> allWineLists=new LinkedHashMap>();

PriorityQueue

Queue queue=new PriorityQueue();

int[] values={1,6,3,8,34,55};

for(int i:values)

queue.offer(i); //Populate

System.out.println(""+queue.peek());//Print highest priority int

System.out.println(""+queue.size());

for(int i:values)

System.out.print(""+queue.poll(i));//Print and remove




A case where Mixing of Generic & non Generic collection creates Havoc




class Old{ //Old, Pre-Generic code

void InsertInt(List lst){

lst.add(new Integer(42));

}

}

class New{ //Newer class, under study

void go(){

List l=new ArrayList();

Old oldobject=new Old();

oldobject.InsertInt(l); //Adds Integer in an ArrayList OK!

}

void nogo(){

List l=new ArrayList();

Old oldobject=new Old();

oldobject.InsertInt(l); //Adds Integer in an ArrayList OOPS!, RuntimeException

}

}

This is primary reason why the compiler 'unhappily' chunks out an -Xlint:unchecked warning on compilation.




Polymorphism in Generics- Not Allowed

Hence you can't use

List mylist=new ArrayList();

Think again, the new ArrayList(); part can be changed by any other object of a class that is-a (instanceof) List implementer, but Generics is STRICTLY a COMIPILE-TIME mechanism, so no polymorphism applies.

A workaround of this problem is that Java allows you to add any subtype of the Type parameter.

Eg:

class Human{}

class Man extends Human{}

class Boy extends Human{}




List l=new ArrayList();

l.add(new Man()); //Allowed

l.add(new Boy()); //Allowed




Using Generic Methods

The above problem applies here also.Consider the following class hierachy.


Suppose, we have the following method,

public void addanAnimal(List an){

an.add(new Humans()); //legal

..

..

}

Now, if you invoke this method as:

List zoo=new ArrayList();

zoo.add(new Humans());

zoo.add(new Humans());

addanAnimal(zoo); //OK, as zoo's type is




but, List monkey=new ArrayList();

monkey.add(new Chimps());

..

..

addanAnimal(monkey);//ERROR,as monkey's type is not

This gives the following error on compilation.

addanAnimal(java.util.List) in "Classname"

cannot be applied to (java.util.List)

addanAnimal(monkey);

^

To avoid such kind of trouble, use a wildcard type parameter in the argument type. It is the programmer's assurance that the method will not ADD/Insert anything to the collection.

So, the method becomes: public void addanAnimal(List an)

This means, any collection can be assigned that is of type Animals or anything lower in its hierachy.

Note:

If you did'nt change the add(); statement, you'll get an error:=

"classname" : cannot find symbol

symbol : method add(Humans)

location : interface java.util.List

an.add(new Humans());

^

2. You can't use , even here, you'll have to make up with extends

3. ADDING IS ALLOWED with wildcard if it uses the super clause i.e. This means, you can add Humans,Animals,Objects in your collections, but not a Man (see object hierachy).

4. List is different from List

= Do not add anything, can be invoked with any type parameter

= Can add, but can be invoked only using Object type parameter

5. is the same as



Generics Introduction



Generics



The generic feature of the java 5 has created quite a
stir in the java community. It is one of the most controversial
addition in java and caused a revolt among some of the experienced
Java developers. However, this new language addition / feature has
gradually been adopted & has become an essential weapon in the
armory of any Java Developer. Even if you are a newbie, you'll need
only a day or two to grasp this concept.



As with any other technology, the 80:20 principle holds
true. 80% of people using generics will only need 20% of its
capabilities. Although generics is much more than a type safety for
Collections, you'll see generics mainly in Collection related codes
and mainly in the new java.util package API's (look in their
javadocs/source codes)







Before the Java 5


Earlier, there was no
way that let a programmer specify that a given Collection would take
a specified object only. So the Collections at that time were not
'type-safe'.


Example of a
pre-Generic code:





List
myList=new ArrayList();


myList.add("A
cool String Object");


myList.add("OK,
can add more Strings");


myList.add(new
Integer(1)); //Only a Wrapper object could enter into a
collection,no primitive


myList.add(new
Float(2.0f));



......



...


Iterator
iterator=myList.Iterator();


while(iterator.hasNext()){





/* One
could only assume(With crossed fingers) that the Iterator here would
retrieve String(or any other specified class)


*/


String
str=(String)iterator.next();


System.out.println(str); //Will
it compile, & then run correctly ?


//Other
String-specific stuff ...





}


Now
the key point here is that the compiler had to depend upon the
assurance of the programmer (by way of casting) that the 'type' of
the collection was indeed what he claimed to be.


In
other words, there was no way that a pre-generic type could function
automatically like the following in java :



int
i="oops";//Will fail at Compile-Time





This
brought about errors/bugs at runtime thereby seriously reducing the
efficiency of developers & increasing the development time of
nearly all Java SE or EE application that used collections. Also, all
this casting code was annoying to write.





Enter
Java 5 / Java Tiger / Java 5.0 / Java 1.5 and Enter Generics


After
the JDK1.5, newer technologies like Generics, for-Each loop,
autoboxing, annotations, etc came about, making life easier for a
java Developer.


Following
the principles of java, a strong-typed language, the generic code
forces a programmer to create a clean code by enforcing compile time
checking instead of getting a Runtime Exception.


The
language syntax with the introduction of generics got wierdier, but
that's what you'll have to do, Angle brackets everywhere<>





List<String>
genlst=new ArrayList<String>();// A generic ArrayList of TYPE =
String


genlst.add("
Hello ");


genlst.add("
World ");





/*


Printing
the contents of the List using the enhanced for loop,the for-Each
loop


*/


for(String
str:genlst) //Note: no Cast() required


System.out.print(genlst);





Now
try adding the following code:





genlst.add(new
StringBuffer("Ouch"));


genlst.add(5); //Ok
to add a primitive, it'll autobox into a Wrapper Class


genlst.add(new
myClass()); //Adding an object of your class


Upon
compilation, the compiler will happily emit following error messages
for the last 3 add operations:





cannot
find symbol


genlst.add(new
StringBuffer("Ouch"));



^


symbol
: method add(java.lang.StringBuffer)


location:
interface java.util.List<java.lang.String>





cannot
find symbol


genlst.add(5);


...
and so on... for every such error


int
i="oops";//Will fail at Compile-Time"





Generics
with an Iterator


If
you are a big fan of Iterator interface or you need that extra
functionality while iterating over your Collection object, you will
get a compile error if you do not parameterize your iterator.


The
following code will display this








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






listOfStrings.add("Happy");



listOfStrings.add("Birthday");



listOfStrings.add("To");



listOfStrings.add("You");






for
(Iterator itr = listOfStrings.iterator( ); itr.hasNext( ); ) {






String
s = itr.next( );



System.out.println(s);



}


But
at compilation, the compiler will emit the following error:


incompatible
types


found
: java.lang.Object


required
: java.lang.String






String
s=itr.next( );



^


To
rectify, change the Iterator declaration as:






for
(Iterator
<String>
itr = listOfStrings.iterator( ); itr.hasNext( ); ) {





What-If
you parameterize Only the Iterator?


Then,
the compiler will give the Unchecked/Unsafe operation warnings and
may crash during runtime as the Collection actually is no type-safe.





What
Generics is not


There
are a lot of misconceptions regarding this xml-like syntax thing in
java amongst many programmers section uncovers some hard truths
regarding generics.


Generics
is a compile time checking.Again I will repeat, Generics is ONLY a
compile time checking/safety mechanism. The actual bytecode of a
generic class will have little or no differences with a non-generic
class.


Actually,
the compiler will test the generic code at compile time & remove
the generic syntax & add the casting code int the bytecode
automatically.





Generics
code can sometimes, even lull you into a false sense of security.
This happens typically when using generics with a legacy(old) code.
The bugs (Remember, they are Runtime) in the legacy code wouldn't be
detected at compile time.In order to reduce this disadvantage, the
javac compiler will emit the following:





Note:
<the packageHierarchy>\<Your class name>.java uses






unchecked
or unsafe operations.





Note:
Recompile with -Xlint:unchecked for details.





There
is also no autoboxing & polymorphism with generic types. So the
following will not compile:





List<int>
list=new ArrayList<int>(); //Use Integer instead





List<Object>
list=new ArrayList<Integer>(); //No polymorphism allowed





Although
in Collection type, you are free to use polymorphism, something which
has been displayed in all examples here.









What next ?


Just
like any language addition, generics is poised to be further
enhanced. With the possible introduction of refied generics in Java 7
it might be the right time to arm yourself with the knowledge of
generics.






More Stuff


If
you like this article, please leave a comment here. Possibly in near
future, I'll introduce more generics stuff such as generic methods,
generic classes,Type Wildcards,using annotations with generics,etc.