Define an abstract Shape class with an area method, and a concrete Circle subclass.

← Classes and OOP · Ref: Q1180

Abstract classes use 'as abstract' and concrete children override:

  Shape as abstract
    area() as abstract
      <- rtn as Float?
    default operator ?
  Circle extends Shape
    override area()
      <- rtn as Float: ...
    override operator ? as pure
      ...
    operator $ as pure
      ...

WHERE 'default operator' CAN GO
Here Shape takes only 'default operator ?', so Circle must NOT add a bare 'default operator': that asks for a generated <=> built on the super's <=>, and Shape has none, so it is E07190 (requires operator '<=>(Shape)'). Circle defines just the operators it needs (? and $).
An abstract class may itself take a full 'default operator', and a child's 'default operator' then compiles. But sameness (== and <=>) is defined once per hierarchy, at Shape, so the child's 'default operator' does not give Circle its own == or <=> - and two Circles with different radii compare EQUAL under Shape's sameness (Circle(2.0) == Circle(3.0) is true). That is a poor fit for a Shape hierarchy, which is why this example does not use it.

See Q103 for abstract classes. See Q116 for default operator.

Example

defines module qa.classesandoop.abstractclassoperators

  defines class

    Shape as abstract
      area() as abstract
        <- rtn as Float?

      default operator ?

    Circle extends Shape
      radius <- 0.0

      Circle()
        -> radius as Float
        this.radius: radius

      override area()
        <- rtn as Float: Float()
        pi <- 3.14159
        rtn: radius * radius * pi

      override operator ? as pure
        <- rtn as Boolean: radius?

      operator $ as pure
        <- rtn as String: `Circle(radius=${radius})`

  defines program

    AbstractClassOperatorsDemo()
      stdout <- Stdout()

      circle <- Circle(5.0)
      stdout.println(`Area: ${circle.area()}`)
      stdout.println(`IsSet: ${circle?}`)

      //Polymorphism with abstract type
      shapes <- List() of Shape
      shapes += Circle(3.0)
      shapes += Circle(7.5)

      for shape in shapes
        stdout.println(`Shape area: ${shape.area()}`)

Common mistakes

E07130 — Concrete subclasses must implement all abstract methods. Omitting the override triggers E07130.

Incorrect:

      //missing override of area()

Correct:

      override area()
        <- rtn as Float: Float()
        pi <- 3.14159
        rtn: radius * radius * pi
Other ways to ask this
  • Write code for an abstract class hierarchy with Shape and Circle
  • I need an abstract Shape with area() and a concrete Circle that provides the implementation
  • In Java I'd use abstract class Shape with abstract double area(). Write the EK9 version
  • Create a Shape/Circle hierarchy showing abstract methods, override, and default operator placement

Coming from another language?

Java: abstract class Shape { abstract double area(); }. Python: class Shape(ABC): @abstractmethod. Rust: trait Shape { fn area(&self) -> f64; }. EK9: Shape as abstract with area() as abstract.

Keywords: abstract, default operator, area, override, shape, circle, class