SLN
SLN

Reputation: 5082

how to cast up to super class when there is an override function in the sub class

A super-class Car and a sub-class Jaguar was created. The function info() -> Void in the sub-class overrided the super-class' function. A instance named theAuto of type Jaguar had been created.

Problem:

Seems I cannot up casts the theAuto to the type of Car, please see the code snippet and its comments

class Car {
        func info() {
                print("You've got a car")
        }
}

class Jaguar : Car {
       override func info() {
               print("You've got a Jaguar")
       }
}

let theAuto = Jaguar()
theAuto.info() // --> You've got a Jaguar
let auto = theAuto as Car // casting but seems not working 
auto.info() // --> You've got a Jaguar
print(type(of: auto)) // fail to casting

Question:

I think I didn't fully understand the concept of casting together with override scenario. Why I cannot make the up casting? Is the override action limited my upper casting?

many thanks for your help and time

Upvotes: 5

Views: 1231

Answers (2)

Hamish
Hamish

Reputation: 80821

Because you're overriding the method in a subclass, you're getting dynamic dispatch. The method implementation to call will be based on the dynamic type of the instance that it's called on. Upcasting a Jaguar to a Car only changes the static type of the instance – the dynamic type is still a Jaguar, for that's the type of instance you created.

Therefore upcasting has no bearing whatsoever on the dynamic dispatch of a method – nor should it, as the whole point of dynamic dispatch is to ensure that the correct method implementation for the given instance is called no matter what it's statically typed as.

The kind of behaviour you're expecting is static dispatch – the compiler chooses the implementation to call based on the static type of the instance. This is commonly achieved by overloading (rather than overriding) functions.

For example, an overloaded static method:

class Car {
    static func info(for car: Car) {
        print("You've got a Car")
    }
}

class Jaguar : Car {
    static func info(for jaguar: Jaguar) {
        print("You've got a Jaguar")
    }
}

let jaguar = Jaguar()
Jaguar.info(for: jaguar) // You've got a Jaguar
Car.info(for: jaguar)    // You've got a Car

let car = jaguar as Car
Jaguar.info(for: car)    // You've got a Car

Here, the compiler resolves which implementation of info(for:) to call based on the static types of what it's being called on and the arguments being passed. If it's either called on Car, or the argument passed is statically typed as a Car, only Car's overload can possibly be statically dispatched to.

Another example of static dispatch is with a protocol extension, where the method isn't a protocol requirement (as making it a requirement gives it dynamic dispatch).

protocol Car {}

extension Car {
    func info() {
        print("You've got a Car")
    }
}

class Jaguar : Car {
    func info() {
        print("You've got a Jaguar")
    }
}

let jaguar = Jaguar()
jaguar.info() // You've got a Jaguar

let car = jaguar as Car
car.info()    // You've got a Car

Here, the compiler resolves which implementation of info() to call solely based on the static type of the instance that it's called on.

Upvotes: 4

James Webster
James Webster

Reputation: 32066

theAuto is always a Jaguar. It is also always a Car. However, calling info() will always return the implementation for the most specific type and so will always print

You've got a Jaguar


Regarding casting:

let car1 : Car = Jaguar()
let car2 : Car = BMW()

if let automobile = car1 as? Jaguar {
    print ("The automobile is a Jaguar")
    car1.info()
}

if let automobile = car2 as? Jaguar {
    print ("The automobile is a Jaguar")
    car2.info()
}

prints

The automobile is a Jaguar
You've got a Jaguar

Only the first section prints. The second car cannot be cast to a Jaguar, because it is a BMW.

Upvotes: 1

Related Questions