Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Case Study Revisited

Let’s see how abstract classes can improve the picture drawing application that we examined in the earlier case study.

Version 2 of the application has a Shape class that looks like this:

open class Shape(val x: Int, val y: Int, val col: Color) {
    open fun draw(context: Graphics2D) {
        // nothing to do here
    }
}

Just so you are clear on why this implementation is not ideal, return to task15_4_2 in your repository and try the following:

  1. Edit Main.kt and add a new line to the code that configures the Picture object:

    add(Shape(0, 0, Color.RED))
    
  2. Rebuild and rerun the application with

    ./amper run
    

    This should compile and run successfully, displaying the same picture as before.

    Caution

    Remember that you need to run the application on your own PC or a School lab machine; it won’t work in a Codespace…

This experiment demonstrates that adding actual Shape objects to a picture is permitted, even though it is meaningless to do so!

Whilst this causes no real problems at run time, it would be nice if we could prevent meaningless code like this from compiling in the first place.

The solution is to make the Shape class abstract:

classDiagram
  Shape <|-- Circle
  Shape <|-- Rectangle
  Picture o-- "0..*" Shape
  class Shape {
    <<abstract>>
    x: Int
    y: Int
    col: Color
    draw(context: Graphics2D)*
  }
  class Circle {
    radius: Int
    draw(context: Graphics2D)
  }
  class Rectangle {
    width: Int
    height: Int
    draw(context: Graphics2D)
  }
  class Picture {
    add(shape: Shape)
    draw(context: Graphics2D)
  }

The implementation of Shape now looks like this:

abstract class Shape(val x: Int, val y: Int, val col: Color) {
    abstract fun draw(context: Graphics2D)
}

Task 16.3

  1. The subdirectory tasks/task16_3 is an Amper project containing Version 3 of the picture drawing application, in which Shape is now an abstract class.

    If you examine Shape.kt, you’ll see the implementation shown above.

  2. Build and run the application with

    ./amper run
    

    You should see a now-familiar picture appear.

  3. Edit Main.kt and add a new line to the code that configures the Picture object:

    add(Shape(0, 0, Color.RED))
    

    This time, you should get a compiler error when you attempt to rebuild the application.