Menu
CoddyTech

Abstract Classes and Methods

Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 23 of 65.

Some classes exist only to be extended. Every shape has an area, but you cannot compute it until you know whether the shape is a circle or a rectangle. Mark such a class @abstract, on the line above class_name:

# shape.gd
@abstract
class_name Shape
extends RefCounted

@abstract func area() -> float
@abstract func perimeter() -> float

func describe() -> String:
	return "Area %.1f, perimeter %.1f" % [area(), perimeter()]

An @abstract func has a signature and no body: no colon and no code after it. It says that every child must provide this method. describe() is a normal method; children inherit it, and it can already call the abstract methods.

You cannot create an object of an abstract class. Shape.new() stops the program before it starts with Parse Error: Cannot construct abstract class "Shape".

A child that writes every abstract method is a normal class:

# rectangle.gd
class_name Rectangle
extends Shape

var width: float
var height: float

func _init(w: float, h: float) -> void:
	width = w
	height = h

func area() -> float:
	return width * height

func perimeter() -> float:
	return 2 * (width + height)
var rect := Rectangle.new(4.0, 6.0)
print(rect.describe())
Area 24.0, perimeter 20.0

If a child leaves one out, Godot reports it before running: Class "Square" must implement "Shape.perimeter()" and other inherited abstract methods or be marked as "@abstract". So every object you can create has area() and perimeter().

An abstract class can still have variables and an _init(). Children call it with super() as usual.

challenge icon

Challenge

Easy

A shop accepts two payment methods. Every payment has an amount and a receipt, but the fee and the name depend on the method.

  • Make PaymentMethod abstract and declare two abstract methods: fee() -> float and method_name() -> String. Write receipt(), which returns NAME: paid AMOUNT, fee FEE, total TOTAL with two decimals for each number (the total is the amount plus the fee).
  • CreditCard: the fee is 3% of the amount (amount * 0.03), and the name is Credit card.
  • BankTransfer: the fee is 1.5, or 0.0 when the amount is 100 or more, and the name is Bank transfer.

The children use the constructor of PaymentMethod. The supplied code reads how many payments follow, then one line per payment: card AMOUNT or transfer AMOUNT. It prints every receipt and the sum of the fees. For card 50 and transfer 120 it prints:

Credit card: paid 50.00, fee 1.50, total 51.50
Bank transfer: paid 120.00, fee 0.00, total 120.00
Total fees: 1.50

Your code goes in payment_method.gd, credit_card.gd and bank_transfer.gd. main.gd holds the supplied input/output code and cannot be edited.

Try it yourself

extends Node

func _ready():
	# Supplied input/output code: keep it as it is
	var count := int(OS.read_string_from_stdin())
	var total_fees := 0.0
	for i in count:
		var p := OS.read_string_from_stdin().split(" ")
		var payment: PaymentMethod
		if p[0] == "card":
			payment = CreditCard.new(float(p[1]))
		else:
			payment = BankTransfer.new(float(p[1]))
		print(payment.receipt())
		total_fees += payment.fee()
	print("Total fees: %.2f" % total_fees)
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online GDScript compiler