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 name as String: String() area() as abstract <- rtn as Float? default operator ? default operator $
Circle extends Shape override area() <- rtn as Float: ... override operator ? as pure ... default operator $
WHERE 'default operator' CAN GO
Shape takes 'default operator ?' and 'default operator $', so Circle can default '$' too: its generated '$' calls Shape's and adds its own field - 'Circle(Shape(name=circle), radius=5.0)'. 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.
AN ABSTRACT '$' ONLY WITHOUT FIELDS
Shape has a field ('name'), so its '$' cannot be abstract: 'operator $ as pure abstract' here is E07185. Below an abstract '$' a sub-type's generated '$' covers only the sub-type's own fields, so Shape's 'name' would silently be left out of every Circle's string. Give the operator a body or 'default operator $' (a hand-written body when the fields are not to be shown). The same holds for '$$' and '#?'. A field-less root, or a trait, may keep '$' abstract - there is nothing to lose.
See Q103 for abstract classes. See Q116 for default operator.
Example
defines module qa.classesandoop.abstractclassoperators defines class Shape as abstract name as String: String() Shape() -> shapeName as String name :=? shapeName area() as abstract <- rtn as Float? default operator ? default operator $ Circle extends Shape radius <- 0.0 Circle() -> radius as Float super("circle") this.radius: radius override area() <- rtn as Float: Float() pi <- 3.14159 rtn: radius * radius * pi override operator ? as pure <- rtn as Boolean: radius? default operator $ defines program AbstractClassOperatorsDemo() stdout <- Stdout() circle <- Circle(5.0) stdout.println(`Area: ${circle.area()}`) stdout.println(`IsSet: ${circle?}`) stdout.println(`Circle: ${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
E07185 — Shape has a field ('name'), so its '$' cannot be abstract: a sub-type's generated '$' below an abstract one covers only the sub-type's own fields, and every Circle's string would silently leave 'name' out. Use 'default operator $' (or give '$' a body) on the type that has the fields; only a field-less root or a trait may keep '$' abstract. See ek9 -h E07185 for details.
Incorrect:
default operator ? operator $ as pure abstract <- rtn as String? Circle extends Shape
Correct:
default operator ? default operator $ Circle extends Shape
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
- Can an abstract class declare operator $ as abstract when it has fields?
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: circle, shape, abstract, default operator, abstract operator, class, override, area