Sunday, December 29, 2013

Abstract class - Why?

Inheritance allows us to create general classes which can be plugged to other classes by using an extends keyword. Let us take a simple example to create classes for variety of shapes.

public class Shape {
    double dim1, dim2;
    Shape(){}
    Shape(double d1, double d2){
        dim1=d1;
        dim2=d2;
    }
    void setDim1(double d1){
        dim1=d1;
    }
    void setDim2(double d2){
        dim2=d2;
    }
    double getDim1() {
        return dim1;
    }
    double getDim2() {
        return dim2;
    }
    void show() {
        System.out.println(getDim1()+","+getDim2());
    }
    public void calcArea(){
        System.out.println("Cannot calculate Area : Shape Unknown");
    }
}

Shape is a general class which can provide common functionality to all types of Shape classes. Let us create a Rectangle Shape

public class Rectangle extends Shape{
    Rectangle(double d1, double d2) {
        super(d1,d2);
    }
    Rectangle(){
       
    }
   public void calcArea() {
       System.out.println("area of rectangle is : "+dim1*dim2);
    }
  
   public void showPerimeter(){
       System.out.println("Perimeter = "+(2*(dim1+dim2)));
   }
}

Let us create another shape, Triangle.

public class Triangle extends Shape{
     Triangle(double d1, double d2) {
        super(d1,d2);
    }
     Triangle() {
        
     }
    public void calcArea(){
        System.out.println("area of traingle = "+(0.5*dim1*dim2));
    }
    public void draw(){
        System.out.println("draw Triangle will be implemented later");
    }
}

Rectangle and Triangle classes override calcArea() method. It makes sense to redefine calcArea(). Both, Rectangle and Triangle Shapes are well defined and so are their functionalities. Let us create a ShapeTester class to test the functionality.

public class ShapeTester {
    public static void main(String[] args) {
        Shape shape= new Shape(20,50);
        Rectangle rectangle = new Rectangle(30,40);
        Triangle triangle = new Triangle(25,12);
        shape.calcArea();
        rectangle.calcArea();
        triangle.calcArea();
    }   
}

run:
Cannot calculate Area : Shape Unknown
area of rect is : 1200.0
area of traingle = 150.0

calcArea() method is insignificant in Shape class. The question is, when there is no well defined functionality for calcArea() method in Shape class we can do two things :

First, we can remove the error message and leave the method with null body.

        public void calcArea(){ }

If calcArea() is null body method, it is misleading. When the method will be called nothing happens.

Second, we can remove the calcArea() method from the Shape class. It seems to be a good alternative. But if we want to design a family of classes with common behavior,  where every type of Shape is capable of calculating the area, depending on the Shape type, we need to find another alternative to deal with the calcArea() method. This is what we can do. We will create calcArea() method in Shape class without any definition. Such a method is called an abstract method. When a class contains one or more abstract methods, class must be declared abstract.

public abstract class Shape {
    double dim1, dim2;
    Shape(){}
    Shape(double d1, double d2){
        dim1=d1;
        dim2=d2;
    }
    void setDim1(double d1){
        dim1=d1;
    }
    void setDim2(double d2){
        dim2=d2;
    }
    double getDim1() {
        return dim1;
    }
    double getDim2() {
        return dim2;
    }
    void show() {
        System.out.println(getDim1()+","+getDim2());
    }
    public abstract void calcArea(){
        System.out.println("Cannot calculate Area : Shape Unknown");
    }
}

Shape class is abstract as it does not define all its methods. An abstract class is incomplete and hence cannot be instantiated. It can only be used as a super class. Any subclass of an abstract class needs to override all the abstract methods in the super class to become a concrete class. If subclass does not override all the abstract methods of its super class(es), subclass remains as an abstract class and hence cannot be instantiated. Abstract methods help to enforce certain functionality in the subclasses. Sometime, even if we have a concrete class and we wish it to be used only as a super class. We can create an abstract class, which does not contain any abstract method. For example :

public class abstract Shape {
    double dim1, dim2;
    Shape(){}
    Shape(double d1, double d2){
        dim1=d1;
        dim2=d2;
    }
    void setDim1(double d1){
        dim1=d1;
    }
    void setDim2(double d2){
        dim2=d2;
    }
    double getDim1() {
        return dim1;
    }
    double getDim2() {
        return dim2;
    }
    void show() {
        System.out.println(getDim1()+","+getDim2());
    }
    public void calcArea(){
  }
}


Now, although we have all the well defined or implemented methods in Shape class, Shape class cannot be instantiated, as it is declared as an abstract class. A class can be declared abstract if we wish to avoid instantiating the class. Such a class can only be used for subclassing. 

By Nancy 
@ 1:55 am 12/30/2013

2 comments:

  1. And what benefit you want to achieve by defining empty method in abstract class?
    Why not keep the method as abstract to ensure it is not produce misleading results when used later through child class or it should through an exception to indicate the calcArea from Shape is not supposed to be used at all.

    ReplyDelete
  2. We don't want to keep an empty method in the class, as it is misleading. It is not a good design. The alternative with a good design is to make method and eventually class as an abstract. It is better design and gives flexibility to implement instance related business logic

    ReplyDelete