Swift Do Subclasses Inherit Initializers

Swift do subclasses inherit initializers?

From the Apple docs:

Subclasses do not inherit their superclass initializers by default.
However, superclass initializers are automatically inherited if
certain conditions are met. In practice, this means that you do not
need to write initializer overrides in many common scenarios, and can
inherit your superclass initializers with minimal effort whenever it
is safe to do so.

Assuming that you provide default values for any new properties you
introduce in a subclass, the following two rules apply:

Rule 1 If your subclass doesn’t define any designated initializers, it
automatically inherits all of its superclass designated initializers.

Rule 2
If your subclass provides an implementation of all of its
superclass designated initializers—either by inheriting them as per
rule 1, or by providing a custom implementation as part of its
definition—then it automatically inherits all of the superclass
convenience initializers.

These rules apply even if your subclass adds further convenience
initializers.

In your case, you did not provide any designated initializer in Child. Therefore, the only one designated initializer has been inherited from Parent.

If you modify the Child class to:

class Child : Parent {
var age : Int
init (age : Int) {
self.age = age
super.init(foo: 10)
}
}

You will see that you cannot create a Child class by calling its superclass initializer.
Read more.

EDIT:

The rules above apply to a more different scenario. Maybe the docs are a little bit confusing for you, but let's just pause and think for a moment. You have a subclass which inherits from a superclass which has one property foo. Now, if your subclass does not add any properties, or adds properties with default values, there is no need for a different initializer than the one from the superclass and so you can safely use it. On the other hand, if you add a property without a default value, you cannot use the initializer from the superclass because the added property needs to be initialized somehow. The compiler will complain and you will have to do one of the two things:

  1. You should implement a designated initializer in the subclass which will assign the new property and also call the designated initializer in the superclass.
  2. You could add a default value to your property and you could still use the designated initializer from the superclass.

Why do we override initializers in Swift?

Subclasses might not inherit their superclass initializers "by default", but they can be inherited (e.g. if you don't supply any designated initializers, you will automatically inherit all of the designated initializers of the superclass ... see Automatic Initializer Inheritance).

Consider a Dog subclass called Chihuahua: If you don't implement any designated initializers in Chihuahua, you automatically inherit the Dog initializers with no extra code on your part. But if you do need to override it for some reason, do so and you just need to call a Dog designated initializer from your Chihuahua designated initializer. And if your Chihuahua initializer has the same signature as a Dog designated initializer, then you must explicitly supply the override keyword.

Can a subclass not inherit the superclass' initializer and when do we use the required initializer?

Try and think about it in terms of what initialization does for an object. It sets values to parameters that do not have values set to them yet that need values set before use. See: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID228. So each class needs to have a way to initialize it and if necessary deal with setting variables or passing that responsibility along the subclass chain. The required init() function should be used as a unique case where somewhere in the initialization chain a special property is computed/set in required init() of a super class that makes it a requirement to call required init() in a subclass/subclasses of it. You do not need to write override required in this case.

Getting around automatic initializer inheritance rules Swift

You should (and must) always call a designated initializer. That's what it means to be the designated initializer. If your designated initializer is init(value:), then init(name: String?, uid: String?, email: String?, username: String?) should be a convenience initializer that calls it. Implementing a new "must-call" (i.e. designated initializer) means that the superclass's designated initializer is invalid for this subclass, but that's not the case here. So just write:

convenience init(name: String?, uid: String?, email: String?, username: String?) {
self.init(values: ["name": name, "uid": uid, "email": email, "username": username])
}

This shouldn't invalidate your other inits.

How to declare a subclass designated initializer with more parameters than superclass?

This has nothing to do with your implementation of init(codImagen:String, respCorrecta:Int, codAyuda: String) (though that implementation is in fact wrong). It has to do with the fact that your superclass has adopted Codable.

Codable requires an implementation of init(from:). Your superclass inherits this through a protocol extension, so there's no problem about the fact that you have not supplied an implementation.

But the subclass is another story. By creating a designated initializer in the subclass, you have killed inheritance. Therefore, your subclass does not inherit the implementation of init(from:) from the superclass. Therefore you must supply it explicitly in the subclass:

class Pregunta: Codable {
var codImagen: String
var respCorrecta: Int
var respUsuario = -1

init(codImagen:String, respCorrecta:Int){
self.codImagen = codImagen
self.respCorrecta = respCorrecta
}
}

class PregRev: Pregunta {
var codAyuda: String
enum CodingKeys : String, CodingKey {
case codAyuda
}
init(codImagen:String, respCorrecta:Int, codAyuda: String){
self.codAyuda = codAyuda
super.init(codImagen: codImagen, respCorrecta: respCorrecta)
}
required init(from decoder: Decoder) throws {
let con = try decoder.container(keyedBy: CodingKeys.self)
self.codAyuda = try con.decode(String.self, forKey: .codAyuda)
try super.init(from:decoder)
}
}

Does a swift subclass *always* have to call super.init()

No, you don't have to.

Assume you have the following classes.

class a {
let name: String

init() {
name = "james"
}
}

class b: a {
let title: String

override init() {
title = "supervisor"
}
}

If you instantiate a variable with

let myVar = b()

Then,

  • override init() in b will be called
  • then the init() in a will be called

Even though you didn't explicitly call super.init().


This has been confirmed by Chris Laettner on the swift-user's email list.
It kicks in when your super class has a single designated initializer with a zero-argument init. This is why you don’t have to call super.init() when deriving from NSObject.

*Thanks to Wingzero's comment below

Swift: Implementing Protocol Initializer in a Class

Surely the designated superclass initializer would be inherited?

No, not always. If the subclass defines its own designated initialisers, then it won't automatically inherit the superclass' designated initialisers. Consider the following example:

class Foo {
init() {}
}

class Bar : Foo {

var str: String

init(str: String) {
self.str = str
}
}

let b = Bar() // illegal – what value would the 'str' property have?

As Bar defines its own init(str:) designated initialiser, it doesn't automatically inherit Foo's designated initialiser init(). This prevents unsafe initialisation in cases where the subclass declares its own stored properties.

Marking init() as required enforces Bar has an init(), be it through providing its own implementation:

class Foo {
required init() {}
}

class Bar : Foo {

var str: String

init(str: String) {
self.str = str
}

// implement required init(), as Bar defines its own designated initialiser.
required init() {
self.str = "foo" // now str is correctly initialised when calling init()
}
}

let b = Bar() // now legal

Or by inheriting Foo's implementation (when Bar doesn't define its own designated initialisers):

class Foo {
required init() {}
}

class Bar : Foo {
// inherits init() from Foo, as Bar doesn't define its own designed initialisers.
}

let b = Bar() // legal


Related Topics



Leave a reply



Submit