Overriding superclass property with different type in Swift
SwiftSwift Problem Overview
In Swift, can someone explain how to override a property on a superclass's with another object subclassed from the original property?
Take this simple example:
class Chassis {}
class RacingChassis : Chassis {}
class Car {
let chassis = Chassis()
}
class RaceCar: Car {
override let chassis = RacingChassis() //Error here
}
This gives the error:
Cannot override with a stored property 'chassis'
If I have chassis as 'var' instead, I get the error:
Cannot override mutable property 'chassis' of type 'Chassis' with covariant type 'RacingChassis'
The only thing I could find in the guide under "Overriding Properties" indicates that we have to override the getter and setter, which may work for changing the value of the property (if it's 'var'), but what about changing the property class?
Swift Solutions
Solution 1 - Swift
Swift does not allow you to change the class type of any variables or properties. Instead you can create an extra variable in the subclass that handles the new class type:
class Chassis {}
class RacingChassis : Chassis {}
class Car {
var chassis = Chassis()
}
class RaceCar: Car {
var racingChassis = RacingChassis()
override var chassis: Chassis {
get {
return racingChassis
}
set {
if let newRacingChassis = newValue as? RacingChassis {
racingChassis = newRacingChassis
} else {
println("incorrect chassis type for racecar")
}
}
}
}
It seems one cannot declare a property with the let syntax and override it with var in it’s subclass or vice-versa, which may be because the superclass implementation might not be expecting that property to change once initialized. So in this case the property needs to be declared with ‘var’ in the superclass as well to match the subclass (as shown in the snippet above). If one cannot change the source code in the superclass then its probably best to destroy the current RaceCar and create a new RaceCar every time the chassis needs to be mutated.
Solution 2 - Swift
This seems to work
class Chassis {
func description() -> String {
return "Chassis"
}
}
class RacingChassis : Chassis {
override func description() -> String {
return "Racing Chassis"
}
func racingChassisMethod() -> String {
return "Wrooom"
}
}
class Car {
let chassis = Chassis()
}
class RaceCar: Car {
override var chassis: RacingChassis {
get {
return self.chassis
}
set {
self.chassis = newValue
}
}
}
var car = Car()
car.chassis.description()
var raceCar = RaceCar()
raceCar.chassis.description()
raceCar.chassis.racingChassisMethod()
Solution 3 - Swift
Try this:
class Chassis{
var chassis{
return "chassis"
}
}
class RacingChassis:Chassis{
var racing{
return "racing"
}
}
class Car<Type:Chassis> {
let chassis: Type
init(chassis:Type){
self.chassis = chassis
}
}
class RaceCar: Car<RacingChassis> {
var description{
return self.chassis.racing
}
}
Then:
let racingChassis = RacingChassis()
let raceCar = RaceCar(chassis:racingChassis)
print(raceCar.description) //output:racing
Solution 4 - Swift
Theoretically, you're allowed to do it this way...
class ViewController {
var view: UIView! { return _view }
private var _view: UIView!
}
class ScrollView : UIView {}
class ScrollViewController : ViewController {
override var view: ScrollView! { return super.view as ScrollView! }
}
class HomeView : ScrollView {}
class HomeViewController : ScrollViewController {
override var view: HomeView! { return super.view as HomeView! }
}
This works perfectly in an Xcode playground.
But, if you try this in a real project, a compiler error tells you:
> Declaration 'view' cannot override more than one superclass declaration
I've only checked Xcode 6.0 GM as of now.
Unfortunately, you'll have to wait until Apple fixes this.
I've submitted a bug report too. 18518795
Solution 5 - Swift
The Solution Dash provided works well except that the super class has to be declared with the let keyword rather than var. Here is a solution that is possible but NOT RECOMENDED!
The solution below will compile with Xcode 6.2, SWIFT 1.1 (if all the classes are in different swift files) but should be avoided because IT CAN LEAD TO UNEXPECTED BEHAVIORS(INCLUDING A CRASH, especially when using non-optional types). NOTE: THIS DOES NOT WORK WITH XCODE 6.3 BETA 3, SWIFT 1.2
class Chassis {}
class RacingChassis : Chassis {}
class Car {
var chassis:Chassis? = Chassis()
}
class RaceCar: Car {
override var chassis: RacingChassis? {
get {
return super.chassis as? RacingChassis
}
set {
super.chassis = newValue
}
}
}
Solution 6 - Swift
I've seen a lot of reasons why designing an API using variables instead of functions is problematic and to me using computed properties feels like a workaround. There are good reasons to keep your instance variables encapsulated. Here I've created a protocol Automobile that Car conforms to. This protocol has an accessor method that returns a Chassis object. Since Car conforms to it the RaceCar subclass can override it and return a different Chassis subclass. This allows the Car class to program to an interface (Automobile) and the RaceCar class that knows about RacingChassis can access the _racingChassis variable directly.
class Chassis {}
class RacingChassis: Chassis {}
protocol Automobile {
func chassis() -> Chassis
}
class Car: Automobile {
private var _chassis: Chassis
init () {
_chassis = Chassis()
}
func chassis() -> Chassis {
return _chassis
}
}
class RaceCar: Car {
private var _racingChassis: RacingChassis
override init () {
_racingChassis = RacingChassis()
super.init()
}
override func chassis() -> Chassis {
return _racingChassis
}
}
Another example of why designing an API using variables breaks down is when you have variables in a protocol. If you'd like to break out all of the protocol functions into an extensions you can, except stored properties cannot be placed in extensions and have to be defined in the class (to get this to compile you'd have to uncomment the code in AdaptableViewController class and remove the mode variable from the extension):
protocol Adaptable {
var mode: Int { get set }
func adapt()
}
class AdaptableViewController: UIViewController {
// var mode = 0
}
extension AdaptableViewController: Adaptable {
var mode = 0 // compiler error
func adapt() {
//TODO: add adapt code
}
}
The above code will have this compiler error: "Extensions may not have stored properties". Here's how you can re-write the example above so that everything in the protocol can be separated out in the extension by using functions instead:
protocol Adaptable {
func mode() -> Int
func adapt()
}
class AdaptableViewController: UIViewController {
}
extension AdaptableViewController: Adaptable {
func mode() -> Int {
return 0
}
func adapt() {
// adapt code
}
}
Solution 7 - Swift
You can achieve it with use of generics:
class Descriptor {
let var1 = "a"
}
class OtherDescriptor: Descriptor {
let var2 = "b"
}
class Asset<D: Descriptor> {
let descriptor: D
init(withDescriptor descriptor: D) {
self.descriptor = descriptor
}
func printInfo() {
print(descriptor.var1)
}
}
class OtherAsset<D: OtherDescriptor>: Asset<D> {
override func printInfo() {
print(descriptor.var1, descriptor.var2)
}
}
let asset = Asset(withDescriptor: Descriptor())
asset.printInfo() // a
let otherAsset = OtherAsset(withDescriptor: OtherDescriptor())
otherAsset.printInfo() // a b
With this approach you will have 100% type safe code without force unwraps.
But that this is kind of a hack, and if you will need to redefine several properties than you class declarations will look like a total mess. So be careful with this approach.
Solution 8 - Swift
Depending on how you plan on using the property, the simplest way to do this is to use an optional type for your subclass, and override the didSet {}
method for the super:
class Chassis { }
class RacingChassis: Chassis { }
class Car {
// Declare this an optional type, and do your
// due diligence to check that it's initialized
// where applicable
var chassis: Chassis?
}
class RaceCar: Car {
// The subclass is naturally an optional too
var racingChassis: RacingChassis?
override var chassis: Chassis {
didSet {
// using an optional, we try to set the type
racingChassis = chassis as? RacingChassis
}
}
}
Obviously you need to spend some time checking to ensure the classes can be initialized this way, but by setting the properties to optional, you protect yourself against situations where casting no longer works.
Solution 9 - Swift
You can simply create another variable of RacingChassis.
class Chassis {}
class RacingChassis : Chassis {}
class Car {
let chassis: Chassis
init(){
chassis = Chassis()
}}
class RaceCar: Car {
let raceChassis: RacingChassis
init(){
raceChassis = RacingChassis()
}}
Solution 10 - Swift
Try this:
class Chassis {}
class RacingChassis : Chassis {}
class SuperChassis : RacingChassis {}
class Car {
private var chassis: Chassis? = nil
func getChassis() -> Chassis? {
return chassis
}
func setChassis(chassis: Chassis) {
self.chassis = chassis
}
}
class RaceCar: Car {
private var chassis: RacingChassis {
get {
return getChassis() as! RacingChassis
}
set {
setChassis(chassis: newValue)
}
}
override init() {
super.init()
chassis = RacingChassis()
}
}
class SuperCar: RaceCar {
private var chassis: SuperChassis {
get {
return getChassis() as! SuperChassis
}
set {
setChassis(chassis: newValue)
}
}
override init() {
super.init()
chassis = SuperChassis()
}
}
Solution 11 - Swift
class Chassis {}
class RacingChassis : Chassis {}
class Car {
fileprivate let theChassis: Chassis
var chassis: Chassis {
get {
return theChassis
}
}
fileprivate init(_ chassis: Chassis) {
theChassis = chassis
}
convenience init() {
self.init(Chassis())
}
}
class RaceCar: Car {
override var chassis: RacingChassis {
get {
return theChassis as! RacingChassis
}
}
init() {
super.init(RacingChassis())
}
}
Solution 12 - Swift
The following allows a single object to be used in base and derived classes. In the derived class, use the derived object property.
class Car {
var chassis:Chassis?
func inspect() {
chassis?.checkForRust()
}
}
class RaceCar: Car {
var racingChassis: RacingChassis? {
get {
return chassis as? RacingChassis
}
}
override func inspect() {
super.inspect()
racingChassis?.tuneSuspension()
}
}
Solution 13 - Swift
A slight variation of other answers, but simpler and safer with some nice benefits.
class Chassis {}
class RacingChassis : Chassis {}
class Car {
let chassis: Chassis
init(chassis: Chassis) {
self.chassis = chassis
}
}
class RaceCar: Car {
var racingChassis: RacingChassis? {
get {
return chassis as? RacingChassis
}
}
}
The benefits include that there is no restriction on what chassis must be (var, let, optional, etc), and it is easy to subclass RaceCar. Subclasses of RaceCar can then have their own computed value for chassis (or racingChassis).
Solution 14 - Swift
Simple using generics, e.g.
class Chassis {
required init() {}
}
class RacingChassis : Chassis {}
class Car<ChassisType : Chassis> {
var chassis = ChassisType()
}
let car = Car()
let racingCar = Car<RacingChassis>()
let c1 = car.chassis
let c2 = racingCar.chassis
print(c1) // Chassis
print(c2) // RacingChassis
Also, Chassis don't even need to be subclasses:
protocol Chassis {
init()
}
class CarChassis: Chassis{
required init() {
}
}
class RacingChassis : Chassis {
required init() {
}
}
class Car<ChassisType : Chassis> {
var chassis = ChassisType()
}
Solution 15 - Swift
just set new imageview property with different naming convention like imgview because imageView is already its own property and we cannot assign 2 strong properties.