One could workaround the problem and use final, but what if the class
meant to be subtypeable? Self simply does not work in this scenario.It works exactly as it should in this scenario. If A isn't final, then by
definition it's impossible for A to make a copy of type Self. I see,
however, what you mean, which is that you wish you could write a protocol
requirement for a function that returns #Self.
What do you mean? Even with the limited `Self` support in Swift 3, I can write a very effective copying paradigm.
protocol Copyable
{
func copy() -> Self
}
class Building: Copyable
{
var floors = 1
required init()
{ }
func copy() -> Self
{
let dynamicType = type(of: self)
let newObject = dynamicType.init()
newObject.floors = self.floors
return newObject
}
}
class Mansion: Building
{
var butlers = 3
override func copy() -> Self
{
// let newObject = super.copy()
let newObject = super.copy() as! Mansion
newObject.butlers = self.butlers
// return newObject
return unsafeBitCast(newObject, to: type(of: self))
}
}
let hoboHouse = Building()
hoboHouse.floors = 0
let beggarHouse = hoboHouse.copy()
print(beggarHouse.floors) // "0"
let myHouse = Mansion()
myHouse.floors = 4
let yourHouse = myHouse.copy()
print(yourHouse.floors) // “4”
Besides the poor support for `Self` in the function body (SE–0068 fixes that), it seems like an acceptable way of doing it.
Of course, I would love being able to use an initializer setup, but there are serious bugs in the implementation.
protocol Clonable
{
init(other: Self)
}
extension Clonable
{
func clone() -> Self
{ return type(of: self).init(other: self) }
}
class Base: Clonable
{
var x: Int
init(x: Int)
{ self.x = x }
required init(other: Base)
{ self.x = other.x }
}
class Derived: Base
{
var y: String
init(x: Int, y: String)
{
self.y = y
super.init(x: x)
}
// Should be required by the Clonable protocol, but it isn't.
required init(other: Derived)
{
self.y = other.y
super.init(other: other)
}
// Required because it was `required` in Base. Even a `Derived` calls this initializer to clone, which is wrong. Bugs abound.
required init(other: Base)
{ fatalError("init(other:) is wrong.") }
}
let me = Derived(x: 1, y: "food")
let alienClone = me.clone() // "init(other:) is wrong."