Title: Polymorphism Shapes
Course: CPS 161: An Intro to Programming with Java - DL MM MASTER

Polymorphism Shapes Example

Definition of Polymorphism:


class Shape
{
     public double area()
     { .....
     }
}
class Circle extends Shape
{
     public double area()
     { ....
     }
}

Shape s;
....
s = new Circle();
....

s.area(); // which area gets called?

Late Binding (Polymorphic approach)
         - the decision gets made at runtime and is based on what the object really is.
         Based on this approach the area method in "Circle" would be called

Early Binding - Compiler makes the decision and would pick the area method in "Shape"


Shape Family of Classes - first version

Shape.java


package shapes;

public class Shape {

    private static int count=0;
    public static int getCount()
    {
        return count;
    }
    public Shape()
    {
        count += 1;
    }

    public double getArea()
    {
        // Hard to calculate Area for an arbitrary Shape
        System.out.println("Sure you wanted the area of Shape?");
        return -1;
    }
    public double getPerimeter()
    {
        System.out.println("Sure you wanted the perimeter of Shape?");
        // Hard to calculate Perimeter for an arbitrary Shape
        return -1;
    }
    public double getAreaPerimeterRatio()
    {
        double ratio = getArea() / getPerimeter();
        return ratio;
    }
}

class Rectangle extends Shape
{
    private double width, height;

    private static int count=0;

    public static int getCount()
    {
        return count;
    }


    public Rectangle(double width, double height)
    {
        this.width = width;
        this.height = height;
        count += 1;
    }

    public double getWidth()
    {
        return width;
    }
    public double getHeight()
    {
        return height;
    }

    public double getArea()
    {
        return width*height;
    }
    public double getPerimeter()
    {
        return 2*(width + height);
    }
}

class Square extends Rectangle
{
    private static int count=0;

    public static int getCount()
    {
        return count;
    }

    public Square(double side)
    {
        super(side,side);
        count += 1;
    }
    public double getSide()
    {
        return getWidth();
    }
}

class Circle extends Shape
{
    private double radius;
    private static int count=0;

    public static int getCount()
    {
        return count;
    }


    public Circle(double radius)
    {
        this.radius = radius;
        count +=1;
    }
    public double getRadius()
    {
        return radius;
    }
    public double getArea()
    {
        return Math.PI * radius * radius;
    }
    public double getPerimeter()
    {
        return 2 * Math.PI * radius;
    }
}

class Triangle extends Shape
{
    private double side_a, side_b, side_c;
    private static int count=0;

    public static int getCount()
    {
        return count;
    }


    public Triangle(double side_a, double side_b, double side_c)
    {
        this.side_a = side_a;
        this.side_b = side_b;
        this.side_c = side_c;
        count +=1;
    }
    public double getSide_a()
    {
        return side_a;
    }
    public double getSide_b()
    {
        return side_b;
    }
    public double getSide_c()
    {
        return side_c;
    }
    public double getArea()
    {
        double s = (side_a + side_b + side_c)/2;
        double area = Math.sqrt(s* (s-side_a)*(s-side_b)*(s-side_c));
        return area;
    }
    public double getPerimeter()
    {
        return side_a + side_b + side_c;
    }
}


class RegularPolygon extends Shape
{
    private int numSides;
    private double side;
    private static int count=0;

    public static int getCount()
    {
        return count;
    }



    public RegularPolygon(int numSides, double side)
    {
        this.numSides = numSides;
        this.side = side;
        count += 1;
    }
    public int getNumSides()
    {
        return numSides;
    }
    public double getSide()
    {
        return side;
    }
    public double getArea()
    {
        double area = (numSides * side * side)  / (4 * Math.tan(Math.PI/numSides));
        return area;
    }
    public double getPerimeter()
    {
        return numSides * side;
    }
}


DemoShapes.java


package shapes;

public class DemoShapes {


    public static void main(String[] args) {
        Shape s = new Shape();
        double area = s.getArea();
        double perimeter = s.getPerimeter();
        System.out.println("area="+ area + " perimeter="+ perimeter);
        System.out.println ("Ratio =" + s.getAreaPerimeterRatio());

        System.out.println("****************   Rectangle   ***************");

        // The textbook calls this Upcasting
        s = new Rectangle(5, 10);
        area = s.getArea();
        perimeter = s.getPerimeter();
        System.out.println("area="+ area + " perimeter="+ perimeter);
        System.out.println ("Ratio =" + s.getAreaPerimeterRatio());


        System.out.println("****************   Square   ***************");

        s = new Square(5);
        area = s.getArea();
        perimeter = s.getPerimeter();
        System.out.println("area="+ area + " perimeter="+ perimeter);
        System.out.println ("Ratio =" + s.getAreaPerimeterRatio());


        System.out.println("****************   Circle   ***************");

        s = new Circle(1);
        area = s.getArea();
        perimeter = s.getPerimeter();
        System.out.println("area="+ area + " perimeter="+ perimeter);
        System.out.println ("Ratio =" + s.getAreaPerimeterRatio());

        System.out.println("****************   Triangle   ***************");

        s = new Triangle(3,3,3);
        area = s.getArea();
        perimeter = s.getPerimeter();
        System.out.println("area="+ area + " perimeter="+ perimeter);
        System.out.println ("Ratio =" + s.getAreaPerimeterRatio());

        System.out.println("****************   Regular Polygon   ***************");

        s = new RegularPolygon(3,3);
        area = s.getArea();
        perimeter = s.getPerimeter();
        System.out.println("area="+ area + " perimeter="+ perimeter);
        System.out.println ("Ratio =" + s.getAreaPerimeterRatio());

        System.out.println("==============  Counts ================================");
        System.out.println("Shape Count = " + Shape.getCount());
        System.out.println("Rectangle Count = " + Rectangle.getCount());
        System.out.println("Square Count = " + Square.getCount());
        System.out.println("Circle Count = " + Circle.getCount());
        System.out.println("Triangle Count = " + Triangle.getCount());
        System.out.println("RegularPolygon Count = " + RegularPolygon.getCount());

        // The above is the preferred approach for accessing static things.  However the following also works:
        // Note that this uses "Early Binding .... the compiler makes the decisions

        Rectangle r = new Rectangle(1,2);
        System.out.println("Rectangle count using an object (non-preferred approach: "+ r.getCount());
        Shape s2 = r; // Note that r and s2 are the same object but the produce different results calling getCount()
        System.out.println("Shape count using an object (non-preferred approach: "+ s2.getCount());


    }

}



For Fun, step through the getAreaPerimeterRation method

DemoShapesArray.java

package shapes;

public class DemoShapesArray {

    public static void whatIsThis(Shape s)
    {

        if (s instanceof Square)
        {
            // The book calls this Downcasting
            Square square = (Square)s;
            double side = square.getSide();
            System.out.println("Square with a side of : "+ side);
        }
        else if (s instanceof Rectangle)
        {
            Rectangle rectangle = (Rectangle)s;
            double width = rectangle.getWidth();
            double height = rectangle.getHeight();
            System.out.println("Rectangle, width=" + width + " height="+ height);
        }
        else if (s instanceof Circle)
        {
            Circle circle = (Circle)s;
            double radius = circle.getRadius();
            System.out.println("Circle with a radius of " + radius);
        }
        else if (s instanceof Triangle)
        {
            Triangle triangle = (Triangle)s;
            double a = triangle.getSide_a();
            double b = triangle.getSide_b();
            double c = triangle.getSide_c();
            System.out.println("Triangle with sides " + a +" " +b + " "+c);
        }
        else if (s instanceof RegularPolygon)
        {
            RegularPolygon regularPolygon = (RegularPolygon)s;
            int numSides = regularPolygon.getNumSides();
            double side = regularPolygon.getSide();
            System.out.println("RegularPolygon with numsides="+numSides + " side="+side);
        }
        else if (s instanceof Shape)
        {
            // What happens if this had been the first thing we had checked???
            System.out.println("Shape is all this thing is");
        }
    }


    public static void main(String[] args) {
        Shape[] arr= new Shape[6];

        arr[0] = new Shape();
        arr[1] = new Rectangle(5,10);
        arr[2] = new Square(5);
        arr[3] = new Circle(1);
        arr[4] = new Triangle(3,3,3);
        arr[5] = new RegularPolygon(5,3);

        for (int i=0; i < arr.length; i++)
        {
            Shape s = arr[i];
            double area = s.getArea();
            double perimeter = s.getPerimeter();
            double ratio = s.getAreaPerimeterRatio();

            System.out.println("**************************************");
            whatIsThis(s);
            System.out.println("area="+ area + " perimeter="+ perimeter);
            System.out.println ("Ratio =" + ratio);
        }


        System.out.println("==============  Counts ================================");
        System.out.println("Shape Count = " + Shape.getCount());
        System.out.println("Rectangle Count = " + Rectangle.getCount());
        System.out.println("Square Count = " + Square.getCount());
        System.out.println("Circle Count = " + Circle.getCount());
        System.out.println("Triangle Count = " + Triangle.getCount());
        System.out.println("RegularPolygon Count = " + RegularPolygon.getCount());

        // The above is the preferred approach for accessing static things.  However the following also works:
        // Note that this uses "Early Binding .... the compiler makes the decisions

        Rectangle r = new Rectangle(1,2);
        System.out.println("Rectangle count using an object (non-preferred approach: "+ r.getCount());
        Shape s2 = r; // Note that r and s2 are the same object but the produce different results calling getCount()
        System.out.println("Shape count using an object (non-preferred approach: "+ s2.getCount());

    }

}



Abstract classes

The getArea and getPerimeter methods in Shape make no sense.

- Constructing a Shape class makes no sense.
- It really only makes sense to implement classes like Rectangle, Circle, etc. which can do something with getArea and getPerimeter
- Make the Shape class abstract and the methods getArea and getPerimeter abstract
- Note that you will now get compile errors when you try to construct Shape, ... replace them with something else.
- An Abstract class is a good fit for the Shape class. This is partly because there is at least one hunk of code that is useful to keep in the abstract class and there is a global count on the number of Shapes.

The final results for both of these approaches can be found in:
polymorphism_shape_abstract_interface.htm

Interfaces -

Although an abstract class is a good fit for the Shape class, there is another paradyme worth considering: interfaces. I have a better example of where an interface could be used that I will show later. However, I can go ahead and structure the Shapes example to use interfaces.

- Make an interface named Shape
- Create a file to contain all of the classes which implement the Shape interface.
- Rework the code.

Java Practice(JPHS):

Start with the abstract Shape class and all of the Classes which extend Shape(Rectangle, Circle, Triangle, RegularPolygon). Create a class called MyShapes that has a main method. Then do the following in your main method:

1. (3 points) Create an array of Shape to contain the following objects:
  - 3 Rectangles - you pick the parameters
  - 2 Circles - you pick the parameters
  - 3 Triangles - you pick the parameters (note that it must be possible to create a triangle out of the lengths for the area method to work). In short this means that none of the lengths can exceed the sum of the other 2 lengths. For example lengths of 1,2, and 4 will not work. Try drawing it and you will see why.
  - 3 RegularPolygons.
  - 2 Squares.
  - Why can't I construct Shape classes?
2. (2 points) Create some code to add up the perimeters of all of your Shapes.
3. (2 points) Create some code to add up the areas of all of your Shapes
4. (3 points) Create Java code which prints out the total number of Shapes, Rectangles, Squares, Circles, Triangles, and RegularPolygons. HINT: use the static getCount methods. No hard coding the numbers here.

Last Modified: December 23, 2014 3:19 PM