How to implement copy constructor in Swift subclass?
The simplest way to do it would simply be to change the name of the subclass initialiser to init(copyFromSquare: Square)
, leaving Square
with the init(copyFrom: Shape)
method intact (as you have contracted by inheriting from Shape
).
You could of course override init(copyFrom: Shape)
, and test whether copyFrom
is a Square
, in which case you take one course of action (set the length), otherwise not.
Note also that you need to set self.length
before you call the super.
class Shape : NSObject {
var color : String
override init() {
color = "Red"
}
init(copyFrom: Shape) {
color = copyFrom.color
}
}
class Square : Shape {
var length : Double
override init() {
self.length = 10.0
super.init()
}
override init(copyFrom: Shape) {
if copyFrom is Square {
self.length = (copyFrom as Square).length
} else {
self.length = 10.0 // default
}
super.init(copyFrom: copyFrom)
}
}
init(copyFrom: Square)
is an overload, not an override, of init(copyFrom: Shape)
. What I mean is that they are unrelated methods because they accept different types. In Swift that's acceptable. In ObjC, that's illegal. There are no overloads in ObjC.
Swift initializers don't automatically inherit. So in Swift, you couldn't try to copy a random Shape
as a Square
. The initializer isn't available. But in ObjC, initializers do automatically inherit (and you can't stop them from doing so). So if you have a method initWithCopyFrom:(*Shape)
, it is required that every subclass be willing to accept it. That means you could (in ObjC) try to create a copy of a Circle as a Square. That's of course nonsense.
If this is an NSObject
subclass, you should use NSCopying
. Here's how you would go about that:
import Foundation
class Shape : NSObject, NSCopying { // <== Note NSCopying
var color : String
required override init() { // <== Need "required" because we need to call dynamicType() below
color = "Red"
}
func copyWithZone(zone: NSZone) -> AnyObject { // <== NSCopying
// *** Construct "one of my current class". This is why init() is a required initializer
let theCopy = self.dynamicType()
theCopy.color = self.color
return theCopy
}
}
class Square : Shape {
var length : Double
required init() {
length = 10.0
super.init()
}
override func copyWithZone(zone: NSZone) -> AnyObject { // <== NSCopying
let theCopy = super.copyWithZone(zone) as Square // <== Need casting since it returns AnyObject
theCopy.length = self.length
return theCopy
}
}
let s = Square() // {{color "Red"} length 10.0}
let copy = s.copy() as Square // {{color "Red"} length 10.0} // <== copy() requires a cast
s.color = "Blue" // {{color "Blue"} length 10.0}
s // {{color "Blue"} length 10.0}
copy // {{color "Red"}
Swift 3
class Shape: NSObject, NSCopying {
required override init() {
super.init()
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = type(of: self).init()
return copy
}
}
class Square: Shape {
required override init() {
super.init()
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = super.copy(with: zone) as! Square
copy.foo = self.foo
......
return copy
}
}