Tuesday, April 19, 2011

Bounding the Parameterized Types

Generics won't be complete if this section is not covered. It is all about bounding parametric types. Till now, we have seen parametric types operate on a single java type like Object or String. Now, let us see how the parametric types can be restricted by applying some constraints over them. For this to be illustrated, let us take a sample application called Animal Actions.
Animal Actions class performs various operations on a given Animal like: making them eat, sleep and run. The first constraint that we see here is that only an Animal object can be passed to the Animal Actions class. Let us represent the Animal class as follows.
Animal.java
package generics.bounds;

public abstract class Animal 
{
    // Some common functionalities here.
}
Note that the Animal class is declared as abstract, meaning that some other concrete class is going to extend this Animal class. Another restriction that we see in Animal Actions class is that they will make the Animals to sleep, eat and run. Since these are behaviors and we may give different representations for the same, let them be modeled as interfaces. Following code shows the interface design for the behaviors,

package generics.bounds;

interface Sleepable 
{
    public void sleep();
}

interface Runnable 
{
    public void run();
}

interface Eatable 
{
    public void eat();
}
Let us give implementation of the above behaviors for some animal, say Dog. The following code snippet is for the implementation of Dog which conforms to eating, sleeping and running behavior.
Dog.java
package generics.bounds;

public class Dog extends Animal implements Sleepable, Runnable, Eatable 
{
    public void sleep()
    {
        System.out.println("Dog is sleeping");
    }

    public void run()
    {
        System.out.println("Dog is running");
    }

    public void eat()
    {
        System.out.println("Dog is eating");
    }
}
Now, let us design the Animal Actions class. The restriction we have on Animal Actions class is that, we should operation on any type of object that is an Animal which can eat, sleep and run. Look into the following Animal Actions class,
AnimalActions.java
package generics.bounds;

public class AnimalActions<A extends Animal & Sleepable & Runnable & Eatable> 
{
    private A animal;

    public AnimalActions(A animal)
    {
        this.animal = animal;
    }
 
    public A getAnimal()
    {
        return animal;
    }

    public void setAnimal(A animal)
    {
        this.animal = animal;
    }
 
    public void doActions()
    {
        animal.sleep();
        animal.run();
        animal.eat();
    } 
}
The declaration of the parameterized class looks like the following,

public class AnimalActions<A extends Animal & Sleepable & Runnable & Eatable
Let us break down the pieces in the above declaration. The first trivial stuff that has to be noted is the declaration of the typed parameter A (which is for Animal). The next set of expressions are imposing restrictions on the typed parameter A. The phrase 'A extends Animal' tells that whatever type we pass for the substitution parameter must extend/implement the Animal class/interface. The type that comes after extends can either be an interface or a class. It is illegal to mention something like the following,

public class AnimalActions<A implements SomeInterface>
Only extends keyword is used for both class as well the interface and not the implements keyword. If we want the parametric type to confirm by more than one classes or interfaces, then every types should be separated by the symbol &. For example, in our case, we want some Animal to eat, sleep and run by implementing the Eatable, Sleepable and Runnable interface. So, we have declared something like AnimalActions<A extends Animal & Sleepable & Runnable & Eatable. To sum up things, the generic class declaration can be interpreted like this; it can be passed with any type that implements/extends Animal, Sleepable, Runnable and Eatable types.
Following code snippet makes use of the above Animal Actions class. In the below code, a new instance of Dog object is created and passed on to the constructor of the Animal Actions class. This is perfectly valid as the Dog class extends the Animal class and it also implements Sleepable, Eatable and Runnable interfaces. It then makes a call to AnimalActions.doActions(), thereby the execution gets directed towards Dog.sleep(), Dog.eat() and Dot.run().
AnimalActionsTest.java
package generics.bounds;

public class AnimalActionsTest 
{
    public static void main(String[] args)
    {
        AnimalActions<Dog> animal = new AnimalActions<Dog>(new Dog());
        animal.doActions();
    }
}

No comments:

Post a Comment

Chitika