Sunday, November 20, 2005

Re: Generics in Java

The other thing is that unlike C++, in Java all the checks (as you said) are done at runtime. Add to this the fact that it has a unified type system where all types (except primitives) inherit from Object makes it even more complicated. Both these are the reason for things like "? extends X" and "? super Y". You don't find these in C++ templates. Everything is checked at compile time so the compiler will warn you if the parameter types don't have expected methods. There's no need to specify that you want parameter types to extend or be the super type of some class.

This generics thread is just going to get more and more complicated. In Java, the compiler checks all* generic code at compilation time.

So if I say,
List<Integer> ints = new ArrayList<Integer>();
ints.add("a String"); //Oops... compilation error

But the bytecode generated will be similar to that generated pre Java 5 with casts inserted by the compiler.

Now there are some cases where you have no option but to make an unchecked cast to make code work. In such cases the compiler is not sure that the code/cast is wrong. So compilation is allowed with an unchecked cast warning.

class UncheckedCast<E> {
 private E[] arr;
 public void meth() {
  arr = (E[])new Object[5]; // unchecked cast

Generics for Java are accompanied by a cast-iron guarantee: no cast inserted by erasure will fail, so long as there are no unchecked warnings. The above illustrates the converse: if there are unchecked warnings, then casts inserted by erasure may fail.

"? extends X" and "? super Y" are Wildcards. They are used to increase the range of Types that can be put in or removed from Generic methods/classes. This is a hack to reduce the effects of Erasure. I do not think this will be there in C++ or C#.

A slight diversion..
Does C# too have primitives? I have come across a few instances where having primitives in Java has hurt. Primitives requires having to make all special conversions and stuff. I suppose SmallTalk, Ruby are pure-OOPs wrt this.

I haven't delved too deep into how C# (.NET) has handled these situations. But I think there will be differences since the two implementations are different... no erasure, so type info is preserved. There is something called a constraint which basically allows you to declare an interface you want the parameter type to implement or inherit from some base class or specify that it has a default constructor...

Java too supports Constraints and its called Bounds. So the example Mohnish mentioned for max can be written in Java as...

public static <T extends Comparable<T>> T max (T a, T b) {
 if( a.compareTo(b) < 0 )
  return b;
 return a;

Btw Comparable is an interface which takes a generic type T.

I don't remember reading about Bounds/Constraints in C++... How is a similar thing done?

About the C# Constraints...

where T : new() T must have a no-parameter constructor

Java doesn't have anything similar. It seems to be an interesting constraint which should be useful in creating types of T even though the Type T is only known at runtime.

where T : class T must be reference type (a class)
where T : class_name T may be either class_name or one of its sub-classes (or is below class_name in the inheritance hierarchy)

Could you give an example of the usage of the above Constraints. To clarify; the first constraint only takes T, and the second takes T or its sub-class.

No comments: