taylorialcom/ ObjectOriented

Abstract Classes

An abstract class typically serves as a parent for multiple subclasses. The abstract class can implement some behaviors to be inherited by the subclasses and declare other behaviors that the subclass must implement.1

This page assumes that you have an understanding of inheritance and interfaces. Please review those pages first.

Abstract Class Declaration

public abstract void draw();

Abstract Class Example

Since there really isn't such a thing as a generic shape, we could rewrite our Shape class example from the inheritance page. Each particular type of shape (like a rectangle or a circle) will need to provide its own implementation of the draw(), erase(), and zoom(), so we declare them as abstract in the Shape class, and don't bother to create a dummy implementation.

import javafx.scene.paint.Color;

public abstract class Shape {

  private Color color;
  protected double xCoord;
  protected double yCoord;

  public Shape() {
    color = Color.WHITE;
    xCoord = 0.0;
    yCoord = 0.0;
    System.out.println("Shape: constructor");
  }

  public abstract void draw();

  public abstract void erase();

  public Color getColor() {
    System.out.println("Shape: getColor");
    return color;
  }

  public void move() {
    xCoord += 2.0;
    yCoord += 2.0;
    System.out.println("Shape: move");
  }

  public void setColor(Color color) {
    this.color = color;
    System.out.println("Shape: setColor");
  }

  public abstract void zoom(double magnitude);

}

Just as an interface is only useful if there exists a class that implements the interface, an abstract class is only useful if there is another class that extends it and implements all of the abstract methods.

Making the Shape class abstract does not require us to modify the Circle and Rectangle classes (except that we no longer call the Shape's erase() method within the subclass' erase() methods

public class Circle extends Shape {

  private double radius;

  public Circle() {
    super();
    radius = 0;
    System.out.println("Circle: constructor");
  }

  public void draw() {
    System.out.println("Circle: draw");
  }

  public void erase() {
    radius = 0;
    System.out.println("Circle: erase");
  }

  public double getRadius() {
    System.out.println("Circle: getRadius");
    return radius;
  }

  public void setRadius(double radius) {
    this.radius = radius;
    System.out.println("Circle: setRadius");
  }

  public void zoom(double magnitude) {
    radius *= magnitude;
    System.out.println("Circle: zoom");
  }

}
public class Rectangle extends Shape {

  protected double height;
  protected double width;

  public Rectangle() {
    super();
    height = 0.0;
    width = 0.0;
    System.out.println("Rectangle: constructor");
  }

  public void draw() {
    System.out.println("Rectangle: draw");
  }

  public void erase() {
    height = 0.0;
    width = 0.0;
    System.out.println("Rectangle: erase");
  }

  public void zoom(double magnitude) {
    height *= magnitude;
    width *= magnitude;
    System.out.println("Rectangle: zoom");
  }

}

Details on References

In order to understand the rationale behind abstract classes, let's rehash some rules regarding references...

Circle ref1;
Shape ref2;
List<Shape> ref3;

Polymorphism

List<Shape> shapes = readShapes("shapeData.txt");
for(Shape shape : shapes) {
  shape.zoom(Math.random()*10.0);
}

Why Abstract Classes are Useful

1

Unless the subclass is an abstract class, in which case it is not required to implement any of the abstract methods declared in the super class.

2

The answer is ROT13 encrypted so that you don't see the answer before thinking about it. Since its likely not worth the hassle to ROT13 decrypt the answer, I'm providing it here as well: Doing this would make it impossible for us to run the sample code in the Polymorphism section because zoom() would not be declared in the Shape class, and therefore, couldn't be called through a Shape reference.