http://stackoverflow.com/questions/29637974/whats-the-difference-between-as-as-and-as
http://stackoverflow.com/questions/27570957/swift-difference-as-string-vs-as-string
Setup
Animal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
class Animal { var is_Kilo : Bool = false var _weight : Double = 0.0 var weight: Double { // computed property get { print("Animal::getWeight") return _weight } set(newWeight) { print("Animal::setWeight") _weight = (is_Kilo) ? newWeight * 2.204 : newWeight } } // stored property. If not set and accessed for the first time, it will run this closure // If set, then this closure does not get executed. lazy var name : String = { print("Animal::name (stored)") var defaultName : String = "NA" return defaultName }() func displayInfo() { print(" ") print("== \(className())::displayInfo() ==") print("name: \(name)") print("weight: \(weight)") print("===========================") print(" ") } // call init separately // designated init() { print("Animal::init()") } // designated 2 init(newName : String, newWeight : Double, kiloMetric: Bool) { print("Animal::init(name : String, weight : Double)") is_Kilo = kiloMetric name = newName weight = newWeight } deinit { print("\(name) Animal::deinit") } func className() -> String { return String(describing: type(of: Animal.self)) } } |
MeatEater
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class MeatEater: Animal { var foods : [String] = ["Beef", "Chicken", "Pork", "Seafood"] var drinks : [String] = ["Water"] // public functions available in parent class will be available override func className() -> String { return String(describing: type(of: MeatEater.self)) } // override parent function displayInfo override func displayInfo() { print("== \(className())::displayInfo() ==") //super.displayInfo() print("\(name) likes to eat \(foods)") print("\(name) drinks only \(drinks)") } public final func listOfMeats() { // cannot be over-ridden print("\(className())::listOfMeats() - \(name) likes to eat:") print("\(foods)") } } |
VeggieEater
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class VeggieEater : Animal { var foods : [String] = ["Grass", "Fruits"] override func className() -> String { return String(describing: type(of: VeggieEater.self)) } override func displayInfo() { //super.displayInfo() print("== \(className())::displayInfo() ==") print("\(name) eats only \(foods)") } public final func listOfVeggies() { // cannot be over-ridden print("\(className())::listOfVeggies() - \(name) likes to eat:") print("\(foods)") } } |
Upcast
The upcast, going from a derived class to a base class, can be checked at compile time and will never fail.
Upcast Example
1 2 3 4 5 6 7 8 |
// declare object of type MeatEater var eater : MeatEater = MeatEater(newName: "crawford", newWeight: 567, kiloMetric: true) // parent class reference points to child class reference object var genericAnimal : Animal = eater // upcast here. we upcast MeatEater to Animal. // access its functionality and properties as the parent class. Good for over-ridden or inherited functionalities genericAnimal.displayInfo() |
Downcast
A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).
Because downcasting can fail, the type cast operator comes in two different forms. The conditional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as!, attempts the downcast and force-unwraps the result as a single compound action.
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
Downcast example
1) To start off, we have a bunch of MeatEaters and VeggieEaters lying around in the heap.
2) We then have an array of Animal reference (base class) pointing to subclasses VeggieEater or MeatEater. We can do this because both VeggieEater and MeatEater are child class of Animal.
3) When we loop through this array of Animals, we need to know whether this Animal reference is a VeggieEater or MeatEater. Essentially, we need to know whether that animal is a meat or veggie eater. In order to do this, we downcast it to MeatEater and VeggieEater.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// 1) // first create different subclassed objects var ricky : MeatEater = MeatEater(newName: "ricky", newWeight: 75, kiloMetric: true) var tom : MeatEater = MeatEater(newName: "tom", newWeight: 67, kiloMetric: true) var monster : MeatEater = MeatEater(newName: "monster", newWeight: 567, kiloMetric: true) var elaine : VeggieEater = VeggieEater(newName: "Elaine", newWeight: 50, kiloMetric: true) var monk : VeggieEater = VeggieEater(newName: "monk", newWeight: 56, kiloMetric: true) var hermit : VeggieEater = VeggieEater(newName: "hermit", newWeight: 66, kiloMetric: true) // 2) var eaters : [Animal] = Array() eaters.append(ricky) // Animal reference point to MeatEater object ricky eaters.append(elaine) // Animal reference point to VeggieEater object elaine eaters.append(tom) // Animal reference point to MeatEater object tom eaters.append(monster) // Animal reference point to MeatEater object monster eaters.append(monk) // Animal reference point to VeggieEater object monk eaters.append(hermit) // Animal reference point to VeggieEater object hermit // 3) // if the downcast is success to MeatEater, then the object is a MeatEater. // we DOWNCAST the Animal reference to the MeatEater to use its exclusive functionality // if the downcast is successful to VeggieEater, then the object is a VeggieEater. // we DOWNCAST the Animal reference to the VeggieEater to use its exclusive functionality for eater in eaters { if let carnivore = eater as? MeatEater { // downcast eater to MeatEater print("------- We found a meat eater ---------") carnivore.listOfMeats() } else if let vegetarian = eater as? VeggieEater { print("--------- We found a veggie eater -----------") vegetarian.listOfVeggies() } } |
as? Type means: cast to this type, if possible, otherwise evaluate to nil
as! Type means: cast to an optional Type, because I know it’s an optional Type. I understand that if it’s not that, a runtime exception is generated