본문 바로가기

스피드 문법정리

17. Inheritance and Polymorphism

Inheritance

class Hierarchy

상속관계에 있는 클래스들은 클래스 계층을 구성

 

 

클래스 계층에서 가장 위에있는 클래스를 Root Class, Base Class라고 함

바로 아래에 있는 클래스는 Base Class를 상속 받고 상속관계에서 위에 있는 클래스를 Parent Class, Super Class라고 함

아래있는 클래스는 Child Class, SubClass 라고 함

Base Class아래에는 하나이상의 SubClass가 존재하지만 위쪽에는  Super Class 존재하지 않음 

여러 SubClass가 공통적인 하나의 Super Class를 상속하는 것은 문제가 없음

2개 이상의 Super Class를 상속받는 것은 불가능 (Multiple Inheritance(다중상속))

 

 

 

 

 

Inheritance

class ClassName : SuperClassName {
}

서브클래스는 슈퍼클래스로부터 맴버를 상속 받음

class Figure {
    var name = "Unknown"
    
    inti(name : String) {
        self.name = name
    }
    func draw() {
        print("draw" \(name)")
    }
}
class Circle : Figure {
    var radius = 0.0
}
let c = Cirecle(name : "Circle")
c.radius
c.name
c.draw()

Final class

모든 클래스는 상속의 대상이 될 수 있으나 경우에 따라 상속을 금지할 때 사용

final class ClassName : SuperClassName {
}

Rectangle클래스가 다른 클래스를 상속받는건 가능하지만 다른 클래스가 Rectangle클래스를 상속하는 건 금지

final class Rectangle : Figure {
    var width = 0.0
    var height = 0.0
}

class Square : Rectangle { // Error
}

Overriding

subclass에서 superclass와 동일한 맴버를 구현하는 것

methods, properties, subscripts, initializers 가능

 

- 상위 구현을 무시하고 새로운 구현할 때 override 사용

class Figure {
    var name = "Unknown"
    init (name : String) {
        self.name = name
    }
    func draw() {
        print("draw \(name)")
    }
}

class Circle : Figure {
    var radius = 0.0
    override func draw() {
        print("hi")
    }
}

let c = Cirecle(name : "Circle")
c.draw() // "hi"

 

- 상위 구현을 기반으로 구현할 때 super사용

class Figure {
    var name = "Unknown"
    init (name : String) {
        self.name = name
    }
    func draw() {
        print("draw \(name)")
    }
}

class Circle : Figure {
    var radius = 0.0
    override func draw() {
    	super.draw()
        print("hi")
    }
}

let c = Cirecle(name : "Circle")
c.draw() // draw Circle, hi

 

- 속성을 override 하는 경우 계산속성, 속성 옵저버 사용 아래 예시는 계산속성을 사용하여 override 하는 예시

읽기 전용 속성을 읽기와 쓰기가 가능한 속성으로 overriding 하는 것은 허용되나 읽기나 쓰기가 가능한 속성을 읽기 전용으로 overriding 하는 것은 허용되지 않음

overriding 에서 super를 self로 쓸 경우 오류가 발생하고 찾기도 힘드니 실수를 조심

class Figure {
    var name = "Unknown"
    init (name : String) {
        self.name = name
    }
    func draw() {
        print("draw \(name)")
    }
}

class Circle : Figure {
    var radius = 0.0
    
    var diameter : Double {
        return radius * 2
    }
    
    override func draw() {
    	super.draw()
        print("hi")
    }
}

let c = Cirecle(name : "Circle")
c.draw() // draw Circle, hi

class Oval : Circle {
    override var diameter : Double {
        get {
            return super.diameter
        }
        set {
            super.radius = newValue / 2
        }
    }

    override var radius : Double {
        get {
            return super.radius
        }
        set {
            super.radius = newValue
        }
    }    
}

 

- 아래 예시는 속성옵저버를 사용하여 override 하는 예시

읽기전용 속성의 경우 속성 옵저버를 사용할 수 없음, 읽기전용 속성의 경우 값이 바뀌지 않는데 속성옵저버를 사용할 수 없음  

변수저장 속성에서만 지원됨

class Figure {
    var name = "Unknown"
    init (name : String) {
        self.name = name
    }
    func draw() {
        print("draw \(name)")
    }
}

class Circle : Figure {
    var radius = 0.0
    
    var diameter : Double {
        return radius * 2
    }
    
    override func draw() {
    	super.draw()
        print("hi")
    }
}

let c = Cirecle(name : "Circle")
c.draw() // draw Circle, hi

class Oval : Circle {
    override var diameter : Double {
        get {
            return super.diameter
        }
        set {
            super.radius = newValue / 2
        }
    }

    override var radius : Double {
        willSet {
            print(newValue)
        }
        didset {
            print(oldValue)
        }
    }    
}

 

override를 금지하기 위해서는 앞에 final을 붙여주면 됨

상속대상에서 제외되는 것은 아님, 접근은 가능

class Figure {
    var name = "Unknown"
    init (name : String) {
        self.name = name
    }
    final func draw() {
        print("draw \(name)")
    }
}

class Circle : Figure {
    final var radius = 0.0
    
    var diameter : Double {
        return radius * 2
    }
    
    override func draw() { // Error
    	super.draw()
        print("hi")
    }
}

let c = Cirecle(name : "Circle")
c.draw() // draw Circle, hi

class Oval : Circle {
    override var diameter : Double {
        get {
            return super.diameter
        }
        set {
            super.radius = newValue / 2
        }
    }

    override var radius : Double { // Error
        willSet {
            print(newValue)
        }
        didset {
            print(oldValue)
        }
    }    
}

let o = Ovarl(name : "Oval")
o.radius

Upcasting & Downcasting

subclass 인스턴스를 슈퍼클래스 형식으로 저장하는 것을 Upcasting

square 인스턴스를 생성할 경우 name, width, height를 저장할 수 있는 공간이 생성 해당 인스턴스를 Figure 클래스로  Upcasting할 경우 Figure 클래스에 선언된 맴버로 접근 범위가 제한 -> 실제로는 width, height 속성이 메모리에 저장되어있지만 Figure 클래스가 인식할 수 있는 name 속성에만 접근가능

동일한 클래스에서 수행된 Upcasting은 안전함 하지만 Downcasting은 불안전

 

Square 클래스는 Figure 클래스로 Upcasting 되어있고 Rectangle 클래스는 Square 클래스의 SuperClass 동시에 Rectangle 클래스는 Figure 클래스 SubClass

Upcasting된 클래스의 SubClass 면서 원본 클래스의 SuperClass로 Downcasting하는 것도 가능

원본클래스보다 아래쪽에 있는 SubClass로 다운케스팅은 안됨

class Figure {
    let name : String
    init(name : String) {
    }
    func draw() {
        print("draw \(name)")
    }
}

class Rectangle : Figure {
    var width = 0.0
    var hegight = 0.0
    override func draw() {
        super.draw()
        print("\(width) x \(height)")
    }
}
class Square : Rectangle {
}
let f = Figure(name : "Unknown")
f.name

let r = Rectangle(name : "Rect")
r.width
r.height
r.name

let s : Figure = Square(name: "Square")
s.width // Error
s.height  // Error
s.name

// Downcasting
let downcastedS = s as! Square
downcastedS.name
downcastedS.width
downcastedS.height

let downcastedS = s as! Rectangle
downcastedS.name
downcastedS.width
downcastedS.height

class Rhombus : Square { 
    var angle = 45.0
}
let dr = s as! Rhombus // Error

Type Casting

Type Check Operator

타입 체크는 런타임에서 실행됨

두 피연자의 형식이 동일하고, 동일한 상속 계층에 오른쪽이 Superclass라면 true 리턴

expression is Type
class Figure {
    let name : String
    init(name : String) {
    }
    func draw() {
        print("draw \(name)")
    }
}

class Rectangle : Figure {
    var width = 0.0
    var hegight = 0.0
    override func draw() {
        super.draw()
        print("\(width) x \(height)")
    }
}
class Square : Rectangle {
}

class Circle : Figure {
    var radius = 0.0
    override func draw() {
        super.draw()
        print("hi")
    }
}

let t = Triangle(name : "Triangle")
let r = Rectangle(name : "Rect")
let s = Square(name : "Square")
let c = Circle(name : "Circle")

r is Rectangle // true
r is Figure // true
r is Square // false

Type Casting Operator

downcasing은 불안정하기 때문에 compile time cast는 사용할수 없음

downcasing은 불안정하기 때문에 옵셔널 바인딩과 함께 써주는 것이 좋음

expression as Type // Compile Time Cast
expression as? Type // Runtime Cast / Conditional Cast
expression as! Type // Runtime Cast / Forced Cast

Upcasting 된 인스턴스에서 메소드를 호출하더라도 실제 형식에서 overriding 한 메소드가 호출됨

class Figure {
    let name : String
    init(name : String) {
    }
    func draw() {
        print("draw \(name)")
    }
}

class Rectangle : Figure {
    var width = 0.0
    var hegight = 0.0
    override func draw() {
        super.draw()
        print("\(width) x \(height)")
    }
}
class Square : Rectangle {
}

class Circle : Figure {
    var radius = 0.0
    override func draw() {
        super.draw()
        print("hi")
    }
}

let t = Triangle(name : "Triangle")
let r = Rectangle(name : "Rect")
let s = Square(name : "Square")
let c = Circle(name : "Circle")

let nsstr = "str" as NSString
// "str" as Int

var upcasted : Figure = s
upcasted = s as Figure

upcasted as? Square
upcasted as! Square
upcasted as? Rectangle
upcasted as! Rectangle

upcasted as? Circle // nil
upcasted as! Circle // Error

if let c = upcasted as? Circle {
}

let list = [t, r, s, c]

for item in list {
    item.draw()
    if let c = item as? Circle {
        c.radius
    }
}

 

Any, AnyObject

Any는 형식에 관계없이 모든 데이터 저장, 참조, 값형식 모두 가능

AnyObject는 참조형식만 저장 가능

Any, AnyObject는 형식에 대한 정보가 없음, 인스턴스를 사용하기 위해서는 타입캐스팅이 필요함

var data : Any = 1
data = 2.3
data = "str"
data = [1, 2, 3]
data = NSString()

var obj : AnyObject = NSString()
obj = 1 // Error

if let str = data as? String {
    print(str.count)
} else if let list = data as? [Int] {
}

Type Casting Pattern

as연산자와 is 연산자를 사용, 범용 형식으로 저장되었거나 Upcasting된 인스턴스를 저장할 때 주로 사용

switch data {
case let str as String:
    print(str.count)
case let list as [Int]:
    print(list.count)
case is Double:
    print("Double Value")
default:
    break
}

Overloading

하나의 형식에서 동일한 이름을 가진 다수의 맴버를 구현할 때 사용

Overloading을 지원하지 않는 언어는 이름으로만 다름을 구별

1. 함수 이름이 동일하면 파라미터 수로 식별

2. 함수 이름, 파라미터 수가 동일하면 파라미터 자료형으로 식별

3. 함수 이름, 파라미터가 동일하면 Argument Label로 식별

4. 함수 이름, 파라미터, Argument Label이 동일하면 리턴형으로 식별

func process(value : Int) {
    print("process Int")
}

func process(value : String) {
    print("process String")
}

func process (value : String, anotherValue : String) {
}

func process(_ value : String) {
}

func process(value : Double) -> Int {
    return Int(value)
}

func process(value : Double) -> String? {
    return String(value)
}

let result : Int = process(value : 12.34)
let result = process(value : 12.34) as Int
struct Rectangle {
    func area() -> Double {
        return 0.0
    }
    
    static func area() -> Double {
        return 0.0
    }
}

let r = Rectangle()
r.area()
Rectangle.area()

'스피드 문법정리' 카테고리의 다른 글

20. Protocol  (0) 2020.06.23
18. Initializer and Deinitializer  (0) 2020.06.22
16. Method and Subscript  (0) 2020.06.19
15. Property  (0) 2020.06.18
14. Structure and Class  (0) 2020.06.18