Overriding superclass property with different type in Swift

Swift

Swift 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

Detail in http://www.mylonly.com/14957025459875.html

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJamesView Question on Stackoverflow
Solution 1 - SwiftDashView Answer on Stackoverflow
Solution 2 - SwiftDavid ArveView Answer on Stackoverflow
Solution 3 - Swiftadimtxg0422View Answer on Stackoverflow
Solution 4 - SwiftaleclarsonView Answer on Stackoverflow
Solution 5 - SwiftLukmanView Answer on Stackoverflow
Solution 6 - SwiftKorey HintonView Answer on Stackoverflow
Solution 7 - SwiftAl ZonkeView Answer on Stackoverflow
Solution 8 - SwiftbrandonscriptView Answer on Stackoverflow
Solution 9 - SwiftAntonView Answer on Stackoverflow
Solution 10 - SwiftFried RiceView Answer on Stackoverflow
Solution 11 - SwiftDombi BenceView Answer on Stackoverflow
Solution 12 - SwiftStickleyView Answer on Stackoverflow
Solution 13 - SwiftRyan HView Answer on Stackoverflow
Solution 14 - SwiftmalhalView Answer on Stackoverflow
Solution 15 - SwiftAli RafiqView Answer on Stackoverflow