[swift-users] Swift Strangeness? Failable initializer overriding non-failable vs. default parameters
swu.tulux at xoxy.net
swu.tulux at xoxy.net
Thu Sep 1 04:36:04 CDT 2016
Hi all,
recently I stumbled upon an interesting and slightly confusing aspect of our favorite language.
I had defined a Cache class to conform to NSCoding and inherit from NSObject. My class’s initializer was declared init?(entries: [Entry]? = nil) at first, giving an option to pre-populate it when called from the NSCoding-conformant initializer but also simply start from scratch in my program. Failability is a valuable feature since the class depends on external resources that could be unavailable. Of course there are other means to model this, but a failable initializer is the most elegant one, I think.
Later on, I decided to make the Entry class private to my Cache class.
Now I had to split the initializer in a private init?(entries: [Entry]?) and an internal or public convenience init?().
I’ll abstract a bit now:
First version:
public class A: NSObject {
public class X { }
public init?(x: X? = nil) { }
}
— all good. I can use it like let a = A() in my program.
Second version:
public class B: NSObject {
private class X { }
private init?(x: X?) { }
public convenience override init?() { self.init(x: nil) } // ERROR
}
Now the compiler complains "failable initializer 'init()' cannot override a non-failable initializer" with the overridden initializer being the public init() in NSObject. This is the same in Swift 2 and 3.
Omitting the override does not work, the compiler now says “overriding declaration requires an 'override’ keyword", so it does count as overriding anyway. Suggested Fix-it is inserting override, giving the other error.
How comes I can effectively declare an initializer A.init?() without the conflict but not B.init?() ?
And: Why at all am I not allowed to override a non-failable initializer with a failable one? The opposite is legal: I can override a failable initializer with a non-failable, which even requires using a forced super.init()! and thus introduces the risk of a runtime error. I always have a bad feeling when I use !, so I try to avoid it whenever possible. I would even accept to get a compiler warning ;-)
To me, letting the subclass have the failable initializer feels more natural since an extension of functionality introduces more chance of failure. But maybe I am missing something here – explanation greatly appreciated.
But I found a workaround. My class now looks like this:
public class C: NSObject {
private class X { }
private init?(x: X?) { }
public convenience init?(_: Void) { self.init(x: nil) } // NO ERROR
}
Now I can use it like let c = C(()) or even let c = C() – which is exactly what I intended at first.
The fact that the new declaration does not generate an error message seems (kind of) legal since it (somehow) differs from the superclass initializer's signature. Nevertheless, using a nameless Void parameter at all and then even omitting it completely in the 2nd version of the call feels a bit strange… But the end justifies the means, I suppose. Elegance is almost preserved.
What do you think?
Sincerely,
Stefan
PS: I already posted this question on Stack Overflow <http://stackoverflow.com/q/38311365/1950945>, but for the more philosophical aspects / underpinnings, it is not the right medium — you are obliged to ask a concrete question there (How to?, not Why?).
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160901/dd5bab13/attachment.html>
More information about the swift-users
mailing list