For example,
In 1.4.X of JDK the following would be allowed at compiled time:
l.add(new String());
List l = new ArrayList();
l.add(new Integer(2));
In other words, we could accidentally place an Integer into a List of Strings.
With generics in Java 5.X+ one could alter the above code to:
l.add(new String());
List<String> l = new ArrayList<String>();
l.add(new Integer(2)); //--- Compile Time Failure
At compilation time, the above code will fail to compile as the List has been defined to contain only the type of "String" and the line showing addition of an Integer will fail. Nice and safe compile time type checking :-).
Consider an Object Holder that can hold an object of any type defined in Java 1.4.X:
public ObjectHolder {
private Object object;
public ObjectHolder(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
}
ObjectHolder stringHolder = new ObjectHolder("Hello world");ObjectHolder integerHolder = new ObjectHolder(new Integer(2));
..What about...the following line:
String string = (String) integerHolder.getObject(); //-----No Compile time warning only Class Cast....run time
With 1.5.X things change for the Better:
public class ObjectHolder<T> {
private T object;
public ObjectHolder(T object) {
this.object = object;
}
public T getObject() {
return object;
}
}
where T holds the type information and we can also use the above to declare type safe Object holders like:
Code using the Object holder could say:
ObjectHolder<String> stringHolder = new ObjectHolder<String>(new String("Hello World"));
or
ObjectHolder<Integer> integerHolder = new ObjectHolder<Integer>(new Integer(4423));
String string = stringHolder.getObject();
Integer integer = integerHolder.getObject();
Note that there is no casts involved in the above code.
However, what we cannot do is add a method in the Object Holder that does the following (for example, not the best) :
public T createNew() {
T other = new T();
return other;
}
Why we cannot do the above is due to erasure in Java. What we mean by erasure is that the Type information is lost in the generated byte code or in other words, the generated class files are like 1.4.X classes without Type info.
Why Sun did the same is to support backward compatibility. Backward compatibility is not to be able to run 1.5.X compiled code on a 1.4.X VM but instead to allow 1.4.X VM compiled code to co-exist with 1.5.X compiled classes and run on a 1.5.X Java VM. Running 1.5.X compiled classes on a 1.4.X VM will result in a class version incompatibility error.
The cost of this backward compatibility is that with 1.5 we do not have access to the Type information at run time.
I have a couple of feelings on this. Maybe 1.5 should not have supported backward compatibility and supported Runtime Type generics retention. However, maybe their thought process is that if they they could drop the backward compatibility at version 1.8 and by then all folks must have converted their libraries to be 1.5.X+ generics ready. A definitely good argument. Maybe on the flip side, they should have released a 1.4.X version that had many of the performance and other enhancements (for loop etc) and have 1.5.X version that was not backward compatible. A generics enabling option in the JDK and JRE might have been another option?
Additionally, if one wanted to run 1.5.X compiled code on a 1.4.X VM, there is always Retroweaver to the rescue :-)...thanks to Richard Rodrigues a former colleague of mine, that I know about this tool.
A confession to those reading this post, all the code above has NOT been tried on a compiler, I just wrote it here..so if it has compilation errors, I shameless blame the blogger that they do not have in built java compiler :-)))) as yet!
Some Links:
Neal Gafter's Blog on erasure
Java Forum Discussion on erasure
No comments:
Post a Comment