Monday, May 18, 2009

Type Safe Classes - 2

In my previous post about Type Safe Classes, we can add two additional public API's



The above two API's look very decent and type safe and there is some hard to find problems here. Lets take this by example; In the below example, we can create a stack of animals and add any Type of animal to the stack. But unfortunately with our published APIs, we can add a Dog to animals [Stack] but cannot add List dogs to same stack using pushAll API, its not allowed (But this is what we want to achive, isn't it.)



Also, there is a similar problem with popAll API as well. Consider the below example, Also we know that its acceptable to collect animal object (any Type) in an Object, but we cannot collect all the animals in List<Object> at once using the above mentioned popAll() API



This wont work either because popAll API only take Collection<E> that means Collection<Animal> in our case. Note that Collection<Animal> is not same as Collection<Object> & similarly Iterable<Animal&tt; is not same as Iterable<Dog>. Question is, if the below code works [we can push any Type of E in the stack]



Below code should have also worked [see below code], for all the Types of E, but it doesn't because thought Animal & Dog has covarient type relationship [the referenced object's type parameter is a subclass], List<Animal> & List<Dog> defines "invariant type relationship", [List<Dog> is not a subtype of List<Animal>]



So, Java 1.5 generics also provide us with wild cards, which is what we have to use here. We have to define a "covarient type relationship" using wild cards.



Our same code which was not working before, will work because we converted our invarient type relationship to covarient type.



One more interesting thing is PECS principle [producer extends, consumer super], what it means is; when we only want to read or use the elements in a collection, we use extends keyword. e.g. pushAll(Iterable<? extends E> elements), we can just read the elements but cannot add anything in elements because the input is producer not a consumer. Even if we had pushAnimal(List<? extends Animal> animals) API, we cannot add any elements, except null, in the collection. So, if we want to add elements in the collection as well, we should use a consumer like popAll(Collection<? super E> collection.

I guess PECS rule is the most tricker of all the rules we have to follow while making our code generic.

No comments: