Tuesday, September 14, 2010

Wild Cards and Generics in Java. Collections vs Arrays.

Once my coworker just asked me what for generics and wild cards in java. And I didn’t answer at once. And I need to investigate and find simple way to explain what for wild cards in java.
Collections are very useful instrument for store and work with data. In previous versions of java collections can replace arrays only partly. But started from version java 5 we can completely replace arrays. Because we can use autoboxing, so it does not matters you put primitives or objects in collections. But we still have some restriction, for example when you declare method for some collections

use(List < Item >)

but you want and can and must use this method not only for List < Item > but also collections with children of Item. Signatures for methods should be as general as possible to maximize utility. If one can replace a type parameter by a wildcard then one should do so.

So you can do it with wild cards.

use(List < ? extend Item >)

For example ItemA extends Item
so we can call method
use(List < ItemA >)but we still have some restriction even here. We can only use collection but we can’t change it.
If you want to change collection you need to use another wild card


use(List < ? super Item >)

You may find it helpful to think of ? extends T as containing every type in interval bounded by null
below and T above (where null is a subtype of every reference type). Similarly, you may think of
? super T as a containing every type in an interval bounded by T below and Object above.

Another one topic is arrays. Arrays are covariant, meaning that type S[] is considered to be a subtype of T[] whenever S is a subtype of T.

Integer[] ints = new Integer[] {1,2,3};
Number[] nums = ints;
nums[2] = 3.14; // array store exception
assert Arrays.toString(ints).equals("[1, 2, 3.14]"); // uh oh!

In this fragment we got runtime exception.
but if we will use wild cards

List
< Integer > ints = Arrays.asList(1,2,3);
List
< ? extends Number > nums = ints;
nums.put(2, 3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); // uh oh!

We got compile time error, and it is more better because we will get to know about error earlier and errors is detected by the compiler.

Apart from the fact that errors are caught earlier, there are many reasons to use collections instead of arrays. Collections are far more flexible than arrays.

I can suggest only one case when array can be more efficient then collection. Arrays of primitives when you avoid boxing can be more efficient but only because compiler. I believe future compilers may optimize collection classes specially.

To summarize, I suggest to use collections rather then arrays expect case of backward compatibility. I believe covariant arrays are an artifact of the lack of generics in earlier versions of Java.

No comments: