mogelbuster mogelbuster - 1 month ago 9
Swift Question

How does swift pass along override method calls on casted inherited classes?

I am learning all about swift, OOP and POP, and I am trying to find out exactly how swift passes along overridden method calls between classes. I can't seem to find specific info on this. The Swift Programming Language manual has an incredibly short section on Overriding Methods. My question is best portrayed in code:

class Life {
func test() { print("test called from Life class") }
func skippedTest() { print("skippedTest called from Life class") }
}
class Animal: Life {
override func test() { print("test called from Animal class") }
}
class Dog: Animal {
override func test() { print("test called from Dog class") }
override func skippedTest() { print("skippedTest called from Dog class") }
}
class Shitzu: Dog {
override func test() { print("test called from Shitzu class") }
}

let life = Life()
let animal = Animal()
let dog = Dog()
let shitzu = Shitzu()
print("--------------1---------------")
life.test()
animal.test()
dog.test()
shitzu.test()
print("--------------2---------------")
let array:[Life] = [life,animal,dog,shitzu]
for item in array { item.test() }
print("--------------3---------------")
life.skippedTest()
animal.skippedTest()
dog.skippedTest()
shitzu.skippedTest()
print("--------------4---------------")
for item in array { item.skippedTest() }
print("--------------5---------------")
let shitzuAsLife:Life = Shitzu()
(shitzuAsLife as! Life).test()


and here is the output:

--------------1---------------
test called from Life class
test called from Animal class
test called from Dog class
test called from Shitzu class
--------------2---------------
test called from Life class
test called from Animal class
test called from Dog class
test called from Shitzu class
--------------3---------------
skippedTest called from Life class
skippedTest called from Life class
skippedTest called from Dog class
skippedTest called from Dog class
--------------4---------------
skippedTest called from Life class
skippedTest called from Life class
skippedTest called from Dog class
skippedTest called from Dog class
--------------5---------------
test called from Shitzu class


My conclusion is that no matter how you cast a class instance within its class hierarchy, it ALWAYS calls the most specialized version of the method. For some reason this isn't what I would expect. I would expect when you call a method on a casted class, you would call the method from the class you casted to. I am left with three questions:


  1. Am I doing something wrong?

  2. If not, is this the intended behavior of swift?

  3. If so, then how on earth could you call the parent classes implementation of an overridden method?



If this question is a duplicate, or a RTFM answer that I somehow missed when I RTFM, then I'll delete it. Just kindly point me in the right direction.

Answer

Am I doing something wrong?

Nope!

If not, is this the intended behavior of swift?

Yes, this is called polymorphism.

If so, then how on earth could you call the parent classes implementation of an overridden method?

You can't, and you shouldn't be able to.

Consider this example:

class Human {
    func eat() {
        print("I just ate a well rounded meal of veggies, meat, potatoes and of course, desert.")
    }
}

class Vegetarian: Human {
    override func eat() {
        print("I just ate a meal of veggies, tofu, and desert. No meat!")
    }
}

If this behaved as you expected, then this code:

let human = Vegetarian() as Human
human.eat()

...would cause a vegetarian to eat meat. They certainly won't be too happy about that.

There are MANY cases in which a subclass requires a different implementation (method) for a particular message (method call) than what the superclass provides. In such a case, giving the ability for consumers of the class to call the superclass method can lead the object into an inconsistent state, as you've violated the design it's supposed to work under.

Your second test, in fact, demonstrates one of the main use cases of polymorphism. You don't quite the effect here because test() doesn't do much of importance. Consider that these animal classes represented entities that could be draw on the screen. The procedure for drawing a cat would be very different from drawing a dog. Now what if I have a scene with multiple cats, dogs, horses, and honey badgers? It would be awfully inconvenient to need an [Cat], [Dog], [Horse], and [HoneyBadger], and separately iterate all of them to call .draw() on each of them. Instead, polymorphism allows me to store an array of [Animal], a common superclass to all of the animals I want to draw. Animal defines a draw() method that doesn't do much, but the fact that it's there guarantees me that I can call draw on any animal. My Cat, Dog, etc. all override the Animal definition of draw(), and provide their own implementation. When I iterate over my array and tell all my animals to draw themselves, they act with their own definition of draw().

In the previous example, polymorphism brought convenience. Here's an example that demonstrates functionality you couldn't otherwise achieve. Consider that my user can click on animals, and drag them around. For me to know which animal to move upon dragging, I need to know which animal was selected. I would do this by having a member variable of my scene, defined as let selectedAnimal: Animal?. Everytime my user clicks on an animal, I set selectedAnimal to the correct animal instance. Everytime they deselect, I set selectedAnimal back to nil. Upon dragging, I can call selectedAnimal?.moveTo(mouseX, mouseY). the moveTo call might be defined differently for Horse (which gallops), Dog (which runs on all 4s), and Cat (which sits around being a lazy

Comments