1

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.

zgjie
  • 2,099
  • 2
  • 21
  • 32
  • Was going to edit my comment but mistakenly deleted it... Anyway, is there any way to capture the default parameter value of the superclass during runtime? E.g. if you know that `a` in the initializer above is assigned to some class property (say, `aa`). In this case you could capture the default value of `a` to, say, some static property of a class (for this example, `static let defaultValue = A().aa`, given that `aa` is accessible), and use this property as the default value of the `override` of the initializer above (e.g. `override init(a: String = Foo.defaultValue) { ... }`). – dfrib Aug 07 '16 at 14:35

1 Answers1

1

(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.

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • If that is a private initializer, how can I override it when I don't even know it. I want to write the subclass in another file. – zgjie Aug 07 '16 at 13:41
  • @zgjie I would presume that you, as a developer, know the implementations of the app classes on a hierarchical level, as your question pertains to this very thing. You never mentioned this in your question, but if the subclass is not defined in the same source file as the superc. (i.e., out of private visibility scope), you could let the private initializers above be internal (given that the two different source files are in the same defining module): many of the stdlib implementations resort to non-private access of methods that ought to be private, due to these limitations in access control. – dfrib Aug 07 '16 at 13:50
  • Actually, I want to assume the superclass is inside a closed source 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. – zgjie Aug 07 '16 at 14:10
  • 1
    @zgjie Consider adding these details to your question, and use this as feedback for the next question your ask (i.e., if you have specific restraints to your question, describe them!). – dfrib Aug 07 '16 at 14:16
  • This works well, but if I keep the "private" directive for the init(b: String) definition from the A class, the definition in the B class fails, saying that the argument is wrong, meaning that the A form isn't seen by the B class, did something change in the Swift specs ? – tontonCD Mar 13 '17 at 17:55
  • @tontonCD thanks for the prompt. Yes, `fileprivate` in Swift 3 is what `private` used to be in Swift 2.2. I'll update the example to be valid for Swift 3. See also e.g. [this Q&A regarding the difference between `private` and `fileprivate`](http://stackoverflow.com/a/39027497/4573247). – dfrib Mar 13 '17 at 18:53