[swift-evolution] [Review] SE-0117: Default classes to be non-subclassable publicly
colin.cornaby at mac.com
Mon Jul 11 02:07:48 CDT 2016
> * What is your evaluation of the proposal?
> * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
Extremely strong -1.
I understand the performance benefit, but it’s extremely problematic if you’ve used other languages like C++ that make the same tradeoff. The performance benefit also seems unnecessary for a lot of code, and if you need the performance, it’s easy enough to mark the class final.
Being able to subclass is critical not only to OOP design, but also Cocoa design. Making classes final by default will mean that a lot of classes will unnecessarily not support subclassing, which can complicate design.
This also strongly affects unit testing by getting rid of one of the easiest ways to mock input and output to a unit. It’s not enough to say third party code should be treated as black box. One of the best ways to test input and output from an object is to subclass the objects it communicates to and override the entry points. The biggest gaps I have in my code coverage are where I talk to non-virtual-by-default C++ methods and I can’t mock the output from those modules to hit all my cases.
As someone who writes an API for my day job, I am perfectly fine marking things as final by hand when necessary (when I finally get to jump to Swift.) It’s not inconvenient at all. I would much rather favor making public inheritance issues easier to surface in Swift. Public inheritance can be tricky, but this feels a bit like cutting off our noses to spite our own face. If you are a responsible public API vendor, you should be thinking about inheritance, or marking as final. And it’s wiping out one of Swift and Obj-C’s major advantages over C++.
The “final should be default because adding final after the fact is destructive” arguments are interesting, but ultimately not convincing to me. If you thought your class was safe for subclassing, but it ultimately wasn’t, this solves nothing. You’ll mark your class as subclassable, and you will ship it, and there will be issues, but it will still be too late to take things back. If you know your class is not safe for subclassing, you should mark it as final. There is no advantage here in that scenario.
This is all part of building a safe public API. Public API design can be difficult, but if you don’t understand safe subclassing designs, you are likely to miss the issues and mark your class as subclassable and ship it anyway. Again, the best way to tackle this is to find better ways to surface the issues. Making final the default still doesn’t solve the core issues of people not understanding the right design.
(I’d, ironically enough, be a lot more open to this proposal if Swift supported more dynamic things like proxies that could let you emulate subclassing without actually doing so. But I think that’s veering away from relatability of the language)
More information about the swift-evolution