I've some classes which inherits from a base class. Usually with c++ classes I used to rewrite constructor in children and then call the parent one.
Is there a way in Swift to call parent constructor when init a subclass without rewriting it?
internal class A {
init(aBool: Bool? = false) {
...
}
}
class B: A {
init(aString: String) {
...
}
}
Having these two classes as example, I'd like to init B using the A constructor:
let obj = A(true)
The rules from Apple state that:
[S]ubclasses 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.
What does all that mean? If you make your subclass B's initializer a convenience initializer, then then B should inherit all of A's designated initializers.
class B: A {
convenience init(aString: String) { ... }
}
If you make B's init a convenience init it will not mask the original init and you will be able to do this...
class A {
var b: Bool? = nil
init(aBool: Bool? = false) {
self.b = aBool
}
}
class B: A {
var s: String? = nil
convenience init(aString: String) {
self.init(aBool: false)
self.s = aString
}
}
let obj1 = A(aBool: true) // obj1 is now an A, obviously.
let obj2 = B(aBool: true) // obj2 is now a B
You'll have to be careful to default or otherwise set all properties, though...
Alternatively, you can override the original init in B, and simply call super.init.
Related
Below is sample code from a playground. What I don't understand is why the b variable in the subclass must be a var type and cannot be a let. Can someone help me understand?
class Base1 {
init() { }
}
class Sub1: Base1 {
let b: Int
override init() {
super.init()
}
convenience init(b: Int) {
self.b = b // Cannot assign to property: 'b' is a 'let' constant
self.init()
}
}
I think this has less to do with subclassing and more to do with the class initializers.
The error only tells half the story, changing b from let to var would show other problems with this class:
Initializers must set an initial value for each stored property of the class. If you override init, you must provide a default value for b.
A convenience initializer must call a designated initializer before accessing self
This is how it looks (thanks to Martin R for the improvements suggested in the comments on how to maintain b as a let constant):
class Base1 {
init() { }
}
class Sub1: Base1 {
let b: Int
convenience override init() {
self.init(b: 5)
}
init(b: Int) {
self.b = b
super.init()
}
}
let one = Sub1(b: 10)
one.b // prints 10
let two = Sub1()
two.b // prints 5
In Swift, designated initializers are used to set up all the stored properties. This is from the official documentation:
Designated initializers are the primary initializers for a class. A
designated initializer fully initializes all properties introduced by
that class and calls an appropriate superclass initializer to continue
the initialization process up the superclass chain.
Convenience initializer are secondary, and needs to call desginated initializer.
You can also define a convenience initializer to create an instance of
that class for a specific use case or input value type. Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.
When you are trying your code, although the error message is not clear at this point, the issue is that you cannot use convenience init to initialize a stored property. You have to make b optional var to make it work.
class Sub1: Base1 {
var b: Int?
override init() {
super.init()
}
convenience init(b: Int) {
self.init()
self.b = b
}
}
Or better way is, as #Martin R suggested in one of the comments, to create another designated initializer and use convenience before overridden init, to give a default value:
class Sub1: Base1 {
let b: Int
init(b: Int) {
self.b = b
super.init()
}
convenience override init() {
self.init(b:5)
}
}
First of all, imho, this has nothing with derived class, you could remove you subclassing and get the similar error.
Second, I think it is due to the "nature" of convenience initializers and their usage. They are "optional" (in usage) initializers, for example to provide shortcut way to initialize from some complex data (like other class or structure). Adding initialization of self.b to convenience init in your sample wouldn't bring anything helpful, as you anyway would need to initialize b in designated init:
class Sub1: Base1 {
let b: Int
// You must init b
// Or ger error: property 'self.b' not initialized ...
init(b: Int) {
self.b = b
super.init()
}
// Do you really need the code below now?
//convenience init(b: Int) {
// self.b = b
// self.init()
//}
}
Thus as designated init must initialize b, the convenience initializer as your wrote it becomes meaningless.
Third, assume it would be allowed. So, as convenience initializer are delegated across not necessarily calling the designated one. Thus it would be possible to do something like that:
...
convenience init(b: Int) {
self.b = b
self.init(c: 10)
}
// later some crazy guy adds his own "convenience" initializer and points your to this one.
convenience init(c: Int) {
self.c = c
if c == 7 {
self.b = 11
}
self.init()
}
Now imagine that you are the "compiler" and tell me where the constant b is set to it's constant value and what value?
Finally, in my understanding the correct usage could be like that:
class Base1 {
init() {}
}
class Sub1: Base1 {
let b: Int
init(b: Int) {
self.b = b
super.init()
}
// Convenience initializer providing default value
override convenience init() {
self.init(b: 7)
}
}
So, my concern is that I don't understand clearly what you really wanted to achieve by allowing let b initialized in convenience init?
class Parent {
var a : Int
init(a : Int) {
self.a =a
}
convenience init() {
self.init(a : 10)
}
}
class Child : Parent {
var b : Int
override init(a : Int){
b = 10
super.init(a : a)
}
}
var child = Child() // i know that convenience initializer is inherited, but why??
child.a // 10
child.b // 10
i know that convenience initializer is inherited, but why??
Just I override all designated initializer? so why is it need?
This is the rule as described in The Swift Programming Language:
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.
Inheriting convenience initializers is not needed if clients of the subclass don’t want to use them (just like inheriting methods and properties is not needed if clients of subclasses don’t use them), but it is the natural thing to do in my opinion.
Inheriting all of the superclass’s API is the default for subclasses, so convenience initializers should be inherited as well if possible. Since convenience initializers are required to call a sibling designated initializer, the compiler can be sure that all properties will be properly initialized only if all designated initializers are overridden in the subclass.
I've found numerous examples for using the Singleton pattern in Swift 3. I'm trying to use this method:
class Records: RailsData {
static let shared = Records()
private init() {}
...
}
When I do, I get the compiler error:
Overriding declaration requires an 'override' keyword
When I add the override keyword, it compiles and everything seems to work. Is the override needed because I'm subclassing RailsData? RailData is an abstract class in that it is not instantiated directly. I also didn't make it a singleton.
I'm trying to make sure I don't get bit later by using the override modifier...
You are not doing anything wrong ;) The override modifier is required because your RailsData superclass declares a designated initializer with the exact same signature:
class RailsData {
init() {...}
...
}
as such, the override modified is made necessary in your subclass. The same thing applies to inherited methods as well. From the The Swift Programming Language book:
When you write a subclass initializer that matches a superclass designated initializer, you are effectively providing an override of that designated initializer. Therefore, you must write the override modifier before the subclass’s initializer definition. This is true even if you are overriding an automatically provided default initializer.
As with an overridden property, method or subscript, the presence of the override modifier prompts Swift to check that the superclass has a matching designated initializer to be overridden, and validates that the parameters for your overriding initializer have been specified as intended.
Rest assured, you won't be bitten by this later on ;)
Motivating example. To see this initializer overriding in action, try this example:
class SuperClass {
init(x: Int) {
print("3. SuperClass.init(x: \(x))")
}
convenience init() {
print("1. SuperClass.init()")
self.init(x: 123) // Will be overridden by subclass.
}
}
class SubClass: SuperClass {
override init(x: Int) {
print("2. SubClass.init(x: \(x))")
super.init(x: x)
print("4. SubClass.init(x: \(x))")
}
}
// Calls inherited convenience initializer.
let sub = SubClass()
outputs:
SuperClass.init()
SubClass.init(x: 123)
SuperClass.init(x: 123)
SubClass.init(x: 123)
Abstract classes. By the way, there is no direct, linguistic support for abstract classes in Swift — nor in Objective-C — but at least Cupertino is thinking about it ;) Currently, such a concept is merely a soft convention used by frameworks authors, etc.
To add to #Paulo Matteo's comprehensive answer, this is most likely the fix you need:
override private init() {
super.init()
}
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Confusion: I provided one designated initializer in sub class. Its means no super class designated initializer will comes down to subclass. But I still needs to write override keyword to provide implementation of any super class designated initializer in sub class(which means super class designated init comes down to sub class). Rule 1 says if I provided subclass designated initializer then super class designated won't comes down. (Override only makes sense if we did not provided any designated init for sub class.)
class Food{
var name : String
init(foodName :String) {
self.name = foodName
}
}
class RecipieIngredient : Food{
var quantity : Int
init(fName :String, quantity :Int) {
self.quantity = quantity
super.init(foodName: fName)
}
override convenience init (foodName :String){
self.init(fName: foodName, quantity: 1)
}
}
It's because init() is not a protected method. So, if you define an init() method in the subclass, the compiler doesn't know whether you mean the init() method of the superclass, or the subclass. 'override' clears this up.
For example, this is a superclass:
class A {
public init(a: String = "aaaa") {
// this class is in a framework, and the subclass use that framework.
......
}
}
Is there a way to write a subclass and override the init with the same default value of parameter a?
The superclass is inside a framework. A project uses this framework, and want to override a function with default parameter value, but it seems there is no way to get the superclass default parameter value in the override function. I can write another function in the subclass, like check if a parameter is nil, then call the superclass with its default value, otherwise, pass the subclass parameter value to the superclass. But I want to find a way to write the exact same function name, that means to override the superclass function.
(This answer is up to date with Swift 3)
Placing the default parameter value in a convenience initializer, overriding the designated initializer onto which this convenience initializer points
You can let the public superclass initializer with a default argument be a convenience initializer, one that will be used as initializer "interface" for the superclass as well as the subclass. This convenience initializer in turn simply calls a fileprivate designated initializer which is the one you override in your subclass. E.g.
public class A {
let a: String
/* public initializer */
public convenience init(a: String = "aaaa") {
self.init(b: a)
}
/* "back-end" fileprivate initializer: implement initializer
logic here, and override this initializer in subclasses */
fileprivate init(b: String) {
self.a = b
print("super:", b)
}
}
public class B: A {
let b: String
override fileprivate init(b: String) {
self.b = "sub_" + b
print("sub:", b)
super.init(b: b)
}
}
/* The following initializations all call the convenience initializer defined in A */
let a = A() // prints> super: aaaa
let b = B() // prints> sub: aaaa, super: aaaa
let c = B(a: "foo") // prints> sub: foo, super: foo
print(a.a) // aaaa
print(b.a) // aaaa
print(b.b) // sub_aaaa
print(c.a) // foo
print(c.b) // sub_foo
In this simple example the subclass overrides all designated initializers of its superclass (here: one designated initializer), which is why also all the convenience initializers of its superclass (here: one) are available (inherited) by the subclass, as described by Rule 2 in the Language Guide - Initialization - Class Inheritance and Initialization - Automatic Initializer Inheritance.
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.