Tuesday, February 23, 2010

Generics and Polymorphism

List<Object> li = new ArrayList<Number>();//1

Why this will give compilation error? Number is sub class of Object, isn't this the way polymorphism works in java.

Let's investigate the problem raised if compiler allows it. If compiler accepts list of type object to refer list of any subclass of object then same will be true for adding any subtype of object to li, as mentioned below in line 2

li.add("SHIV"); //2

In essence, if compiler doesn't stop it at line 1 while assigning, it will never be in position to stop you from putting string into Number list. So correct way will be

List<Object> li = new ArrayLLIst<Object>();

i.e. List<T> li = new ArrayList<T>(); implies T in both side should be exactly same.

But this look like a serious restriction, how to get away with this(I mean how to achieve polymorphism in generics)? Java comes with wild card to solve these issue to some extent.

List<?> li = new ArrayList<Number>();//3
List<? extends Object> li = new ArrayList<Number>();//4
List<? super Boolean> li = new ArrayList<Number>();//5

In line 3 li can be assigned to ArrayList on any type, n line 4 li can be assigned to any type which extends object and line 5 can refer to any type super class of Boolean. It's evident from the above examples that wild card (?) get us polymorphism in case of generics. But polymorphism in generics comes with some restrictions. Let's discuss them.
List<?> li = new ArrayList<Number>();

li can be assigned to any type but nothing can be added to li. Because compiler will be aware of only reference type, and that is wild card(not a type).

List <? extends Number> li= new ArrayList<Boolean>();

Here li can be used to refer any object that extends Number. What about adding objects to li? Think carefully and answer. If your answer is any object extends Number can be added, then you are wrong. Actually you cannot add object of any type to li. Because here again compiler doesn't know which type exactly li is referring to. Without the restriction you may end up creating list of Boolean and add Number to it.

List <? super Number> li = new ArrayList<Number>();
What about this, as you might have guessed that li can refer to Number or any super class of it. But the big deal will always be about adding element to li. Good news is that, this is only combination with wild card where elements can be added. Compiler is sure that type of li will be either Number or any super class of Number. So if you add Number or sub class of it, it's perfectly fine.

Lets summarize it....

What li can refer to
What can be added to li

List<T> li = new ArrayList<T> ()
li can refer only ArryList of type T T or any sub class of T can be added to li

List<?> li = new ArrayList<T> ()
li can refer any type Nothing can be added to li

List<? extends T> li = new ArrayList<T> ()
li can refer ArrayList of type T or any sub type of T Nothing can be added to li

List<? super T> li = new ArryList<T>()
li can refer ArrayList of type T or any supertype of T T or any sub type of T can be added to li

Tuesday, February 2, 2010

A Date with java.util.Date

Recently I encountered with interesting problem where I wanted to change java.util.Date from one time zone to other. It shouldn't be surprising that its not possible! why ? carry on reading.

When we instantiate Date it's instantiated as System.currentTimeMillis() from the OS. That represents time in mills from a reference date. But then things gets confusing when we try to print this. Lets do one experiment, run same code with different zone selected from your computer.

Date d = new Date();
System.out.println("date "+d);

This will print different sting for same date. This is because toString() implementation of Date takes default time zone of computer to print time zone. If it doesn't find any then it will get GMT time for the date instance.

What to do if we want to display date in time zone other than JVM/OS. We know that Date cannot be used to do that.
What the code below doing?

Date d = new Date();
Calendar c = Calendar.getInstance();
d = c.getTime();

I guess you answered nothing. That's correct, the whole point is Date doesn't store time zone information. But question about displaying Date in zone other than JVM remains unanswered. Let's answer it.

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String IST_Date = df.format(d);
System.out.println("DATE if IST zone "+IST_Date);

Now what if I want to create Date instance from IST_Date string.

Date d = df.parse(IST_Date);