Subclass initializers and final
Imagine an animal's card at a vet clinic. The front desk writes the name on it, and every animal has that field. A parrot also has a field of its own: how many words it knows. You fill it in, and you do it before handing the card to the front desk. From there the card goes straight to the vet, and the vet may look at any field.
Dog from the lesson on inheritance managed without its own init: it got init(name:) from Animal. That won't work for a parrot. It has its own property words without a default value, and the parent's init(name:) knows nothing about it.
Its own init and super.init
If a subclass has its own property without a default value, it needs its own init. There you first set your own properties and then call super.init, so that the parent sets its own:
class Animal {
var name: String
init(name: String) {
self.name = name
}
}
class Parrot: Animal {
var words: Int
init(name: String, words: Int) {
self.words = words
super.init(name: name)
}
}
let kesha = Parrot(name: "Kesha", words: 20)
print(kesha.name, kesha.words) // Kesha 20Let's go through the program:
var words: Intis a property ofParrotitself, and it has no default value.init(name: String, words: Int)takes two values: the name for the parent and the number of words for itself.self.words = wordssets the property ofParrot— the parrot's field on the card.super.init(name: name)calls the parent'sinit, and the parent setsname— the name the front desk writes in.printprintsKesha 20: the parrot has both the inherited propertynameand its ownwords.
With its own init, Parrot no longer inherits init(name:). The call Parrot(name: "Kesha") does not compile, because it lacks words:
error: missing argument for parameter 'words' in callWhy your own properties come first
Swift requires every property of the subclass to have a value by the time super.init is called. The reason lies in what the parent may do.
Once it has set its own properties, the parent's init may call a method of the instance, for example speak(). If Parrot overrides that method, the Parrot version runs: the version is chosen by the class of the instance itself. And if it reads words, the value must already be there. This is the vet who may look at any field of the card. The order is required always, even when the parent calls no methods at all.
Here the clinic picture falls short. The front desk asks for the name itself, but in Swift the subclass passes it on: super.init(name: name) hands the parent the name parameter that the parrot's init received.
No inheritance: final
The word final forbids inheriting and overriding:
- a
final classcannot be inherited from; - a
final funcmethod cannot be overridden in a subclass.
This is how the author of a class protects it from changes they did not plan for. For example, if you declare final class Parrot: Animal, the line class Macaw: Parrot {} does not compile:
error: inheritance from a final class 'Parrot'Interviewers also ask about final and speed. Without it, the method's version is chosen while the program runs, by the class of the instance. A subclass cannot write its own version of a final method, so the compiler can call it directly, without that choice.
Common mistake
The subclass init calls super.init first and sets its own property afterwards:
class Cat: Animal {
var lives: Int
init(name: String, lives: Int) {
super.init(name: name) // ✗
self.lives = lives
}
}error: property 'self.lives' not initialized at super.init callThe program does not compile: when super.init is called, lives has no value yet. Swap the two lines: first self.lives = lives, then super.init(name: name).
Key points
- A subclass
initfirst sets its own properties, then callssuper.init; in the reverse order the program does not compile. - The order matters because the parent's
initmay call an overridden method, and that method may read a subclass property. - A subclass with its own
initdoes not inherit the parent'sinit:Parrot(name: "Kesha")does not compile. - A
final classcannot be inherited from, and afinal funccannot be overridden.