[swift-evolution] [swift-evolution-announce] [Review] SE-0117: Default classes to be non-subclassable publicly

Charlie Monroe charlie at charliemonroe.net
Sun Jul 10 01:59:22 CDT 2016

> On Jul 10, 2016, at 1:37 AM, Károly Lőrentey via swift-evolution <swift-evolution at swift.org> wrote:
> On 2016-07-09 04:39:01 +0000, Jordan Rose via swift-evolution said:
>> I wanted to share a concrete use case that Daniel Dunbar relayed to me. He was working on a closed class hierarchy like the ones discussed here, where all of the subclasses are within a single module, but they are all public. The class also has a required initializer for dynamic construction, so that they could write something like this:
>> internal struct ModelContext { /*…*/ }
>> public class ModelBase {
>>   internal required init(context: ModelContext) { /*…*/ }
>>   // …
>> }
>> public class SimpleModel: ModelBase {
>>   internal required init(context: ModelContext) { /*…*/ }
>> }
>> public class MoreComplicatedModel: ModelBase { /*…*/ }
>> // (within some other type)
>> public func instantiateModelObject<Model: ModelBase>(_ type: Model) -> Model {
>>   return type.init(context: self.context)
>> }
>> That is, a public entry point calls a required initializer with an internal argument type. This is the only way to instantiate Model objects, and the internal context type doesn’t leak out into the public API.
>> Of course, Swift doesn’t allow this. If someone outside of the module subclasses ModelBase, there’s no way for them to provide the dynamically-dispatched 'init(context:)’, because they don’t have access to the internal ModelContext. The author of the library has to make the required initializers public, and either set the ModelContext separately or make it public as well. Even though no one outside the module should be using these APIs.
> Can you remind us why does Swift need required initializers to have the same access as the containing class?

Because without required initializers you can't do something like:

let cl: MyClass.Type = ...
let instance = cl.init(value: 0)

where MyClass has a required init(value: Int).

The issue here is that in order to be able to do this, subclasses that implement another initializer must implement this one as well - so it must be visible outside of the module if it is public.

> Having only package-internal constructors in the public base class is the usual pattern for exporting a sealed class hierarchy in some other languages, like Java.
> -- 
> Károly
> @lorentey
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

More information about the swift-evolution mailing list