Abstract classes
Problem
Zero square meters
You are writing a program that orders tiles for a terrace. The terrace has three parts: a 4 × 3 m rectangle, a round part with a radius of 1 m, and a triangle with a base of 2 m and a height of 3 m. There is a parent Shape with Area(), which returns 0, and each shape overrides it.
The colleague who wrote Triangle forgot:
class Triangle : Shape{ public double Base { get; private set; } public double Height { get; private set; }
public Triangle(double baseLength, double height) { Base = baseLength; Height = height; }}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };double total = 0;foreach (Shape s in terrace){ total += s.Area();}Console.WriteLine($"Tiles: {total:F2} m²");Tiles: 15.14 m²You need 18.14 m². The program gives no error, and the order is 3 m² short. The parent's zero has hidden the missing method.
And what is new Shape()? A shape with no shape. An object like that has no reason to exist.
The idea
Abstract class: a promise the child classes keep
Shape is not a real shape. All it says is "every shape has an area". So make it abstract:
abstract class Shape{ public abstract double Area();}- An abstract method has no body, only a header and
;. You cannot give a sensible area to "a shape in general", so you give none. - An abstract class cannot have objects:
new Shape()does not compile. - Every child class has to write the method. Otherwise the program does not compile.
Now the forgotten Triangle gives an error at compile time:
"'Triangle' does not implement inherited abstract member 'Shape.Area()'". The child class writes the method with override, just as with virtual.
More precisely, if you are curious
- A child class that does not write the abstract method can be abstract too. Then the promise passes on to its own child classes.
- A class can be abstract without a single abstract method. That way you only forbid objects of it.
See it
The triangle keeps the promise
Add the method to Triangle:
class Triangle : Shape{ // ... Base, Height and the constructor
public override double Area() { return Base * Height / 2; }}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };double total = 0;foreach (Shape s in terrace){ total += s.Area();}Console.WriteLine($"Tiles: {total:F2} m²");Tiles: 18.14 m²The loop is the same. The Shape[] array holds shapes of three classes, and s.Area() calls the method of each one. The polymorphism from the last lesson works with an abstract parent too.
Try it
Find the bug
Pick the line with the bug.
new Shape() is an attempt to create an object of an abstract class. The first line is fine: new Shape[3] creates an array of three empty slots for shapes, not shapes. You can put a rectangle, a circle or a triangle in them.
The idea
Abstract, but not empty
An abstract class can have everything an ordinary one has: fields, a constructor, ordinary methods. Let every shape have a name and a method that describes it:
abstract class Shape{ public string Name { get; private set; }
public Shape(string name) { Name = name; }
public abstract double Area();
public string Describe() { return $"{Name}: {Area():F2} m²"; }}Describe() is written once, in Shape. It calls Area(), which Shape does not have yet. That is not a problem: Describe() is always called on a real shape, and a real shape has an area.
You do not make objects from Shape, but its constructor is useful: the child classes call it.
class Circle : Shape{ public double Radius { get; private set; }
public Circle(double radius) : base("circle") { Radius = radius; } // ... Area() as before}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };foreach (Shape s in terrace){ Console.WriteLine(s.Describe());}rectangle: 12.00 m²circle: 3.14 m²triangle: 3.00 m²The parent writes what is shared (the name, the description), and the child classes write what is different (the area).
public abstract double Area();
public string Describe(){ return $"{Name}: {Area():F2} m²";}
Shape s = new Circle(1);Console.WriteLine(s.Describe());Step by step, in words
- Shape promises Area() but has no body. There can be no object that is only a Shape.
- The object is a circle: the Circle part has its own Area(). It keeps the promise.
- s.Describe() is written in Shape and works for every shape.
- It calls Area(). The object is a circle, so the Circle method runs: π · 1 · 1.
- Describe() puts the text together. What is shared lives in the parent, what differs lives in the child class.
- The output is the area of the circle, with two digits after the decimal point.
Try it
Whose method?
What will the program print?
// Shape and Circle are from the card above
Shape s = new Circle(2);Console.WriteLine(s.Describe());The variable is of the abstract type Shape, and that is allowed. The object is a circle with a radius of 2. Describe() belongs to Shape, but the Area() inside it is the circle's: π · 2 · 2 ≈ 12.57.
Try it
What is missing?
What has to go in the blank so that you cannot create an object of Shape?
class Shape
{
public abstract double Area();
}abstract. Once a class has an abstract method, the class itself has to be abstract. Otherwise the program does not compile.Exercise
Shapes and animals
Solve the tasks in your editor.
1. Area and perimeter. An abstract class Shape with a name and two abstract methods: Area() and Perimeter(). Child classes Circle (radius) and Rectangle (width and height). Put a circle with a radius of 5 and a 4 × 2.5 rectangle in an array and print both values with two digits after the decimal point.
Circle: area 78.54, perimeter 31.42Rectangle: area 10.00, perimeter 13.002. Animals. An abstract class Animal with an abstract method Sound() and an ordinary method Breathe() that prints "The animal breathes." Child classes Cat and Dog. Call the sound of both in a loop, then Breathe() on the first one.
Meow, meow!Woof, woof!The animal breathes.Exercise
Vehicles and performances
3. Vehicles. An abstract class Vehicle with an abstract method Move(). SimpleCar and Bicycle inherit from it. Bicycle also has its own method Pedal().
The car sets offThe bicycle sets offPedalinggarage[1].Pedal(). Why does it not compile, even though there is a bicycle there?4. Performances. An abstract class Performance with a title and an abstract method Start(). DanceMove and Song inherit from it. Song also has a method Sing().
Starting the dance moonwalkStarting the song 'Bohemian Rhapsody'Singing 'Bohemian Rhapsody'More tasks
- Triangle. Add
Trianglewith its three sides to task 1. The perimeter is their sum, and the area comes from Heron's formula: s = perimeter / 2, area = √(s(s − a)(s − b)(s − c)). For sides 3, 4 and 5 the area is 6.00 and the perimeter is 12.00. - Abstract employee. Make
Employeefrom the last lesson abstract, with an abstractMonthlyPay(). AddFullTime(the salary is paid in full) and keepManagerandIntern. What does the program gain? What does it lose?
Test