The only partially satisfying realization in my opinion looks like this:
protocol Copyable: AnyObject {
init(_ copy: Self)
}
class A: Copyable {
var bar = 21
init() {
}
required init(_ copy: A) {
self.bar = copy.bar
}
}
class B: A {
var baz = 22
override init() {
super.init()
}
required init(_ copy: B) {
self.baz = copy.baz
super.init(copy)
}
@available(*, unavailable)
required init(_ copy: A) {
fatalError()
}
}
final class C: B {
var qux = 23
override init() {
super.init()
}
required init(_ copy: C) {
self.qux = copy.qux
super.init(copy)
}
@available(*, unavailable)
required init(_ copy: B) {
fatalError()
}
}
This approach is good because it forces us to implement the initializer we need in every possible subclass. But with that it brings one big headache, the requirement, in view of the required, it is also necessary to redefine the copy initializer inherited. We have to define it, mark it as inaccessible and provide a repeated implementation everywhere. The main question is why should we do it?
Also, due to the special rules of initializer inheritance, we have to drag the most common default initializer through the whole hierarchy. But this is if you need it, of course.
The alternative realization is absolutely as far as I am concerned does not tolerate any comment:
protocol Copyable: AnyObject {
func copy() -> Self
}
class A: Copyable {
var bar = 21
func copy() -> Self {
precondition(type(of: self) == A.self)
return A(bar: bar) as! Self
}
init(bar: Int = 21) {
self.bar = bar
}
}
class B: A {
var baz = 22
override func copy() -> Self {
precondition(type(of: self) == B.self)
return B(baz: baz, bar: bar) as! Self
}
init(baz: Int = 22, bar: Int = 21) {
self.baz = baz
super.init(bar: bar)
}
}
So why is it so complicated and extremely inexpressive? Is there really no way to avoid such a headache for developers? And is the language itself planning to do something about it?
Something similar: