확장(Extensions)
- 확장은 기존의 타입(클래스, 구조체, 열거형, 프로토콜)에 새로운 기능을 추가하는 개념이다
- 상속과는 다르게 기존 타입의 코드를 변경하지 않고, 새로운 기능을 추가하는 방식이다
상속과 확장의 차이
상속 (Inheritance)
- 수직적인 개념으로 기존 타입을 상속받아 새로운 타입을 정의한다
- 기존 타입의 속성과 메서드를 물려받아 새로 정의하거나, 기존 메서드를 재정의(Override)할 수 있다
- 기존 타입의 구조를 확장하면서도 기존 기능을 변경할 수 있다
- 예)
Person 클래스를 상속하여 Student 클래스를 만들고, 메서드를 재정의하거나 새로운 속성을 추가한다
확장 (Extension)
- 수평적인 개념으로 기존 타입에 새로운 기능을 추가한다
- 기존 기능을 재정의(Override)할 수 없고, 새로운 기능을 추가하는 것만 가능하다
- 기존의 타입을 수정하지 않고도 기능을 확장할 수 있는 강력한 기능이다
- 예)
Int 타입에 새로운 기능을 추가하거나, 기존 클래스에 새로운 메서드를 추가한다
- 확장에서 구현한 메서드는 기본적으로 재정의가 불가능하다 (하지만
@objc를 붙이면 가능)
확장 문법 (Extension Syntax)
struct Calculator {
var value: Int
}
extension Calculator {
func squared() -> Int {
return value * value
}
func cubed() -> Int {
return value * value * value
}
}
let calculator = Calculator(value: 3)
print(calculator.squared())
print(calculator.cubed())
Calculator 구조체는 value라는 저장 속성을 가진다
- 확장을 사용하여
Calculator 타입에 squared()와 cubed() 메서드를 추가
- 기존의
Calculator 인스턴스에서도 새로 추가된 메서드를 사용할 수 있다
- 확장은 기존 타입을 수정하지 않고도 기능을 확장할 수 있는 방법을 제공한다
예시를 통한 상속과 확장의 차이 이해
class Animal {
var name: String
init(name: String) {
self.name = name
}
func sound() -> String {
return "동물 소리"
}
}
class Dog: Animal {
var breed: String
init(name: String, breed: String) {
self.breed = breed
super.init(name: name)
}
override func sound() -> String {
return "멍멍"
}
}
extension Dog {
func run() -> String {
return "\(name)가 달린다."
}
}
let myDog = Dog(name: "바둑이", breed: "진돗개")
print(myDog.sound())
print(myDog.run())
Animal 클래스는 모든 동물의 공통 특성인 name과 sound() 메서드를 정의
Dog 클래스는 Animal 클래스를 상속받아 breed 속성을 추가하고, sound() 메서드를 재정의
- 상속을 사용하여 기존 기능을 수정하고 확장할 수 있는 점을 보여준다
- 확장을 사용하여
Dog 클래스에 run() 메서드를 추가 (새로운 기능만 추가 가능)
- 기존의
Dog 인스턴스에서도 새롭게 추가된 기능을 사용할 수 있다
확장에서 구현한 메서드 재정의 가능 여부 (@objc 사용)
class Student {
var name: String
init(name: String) {
self.name = name
}
}
extension Student {
@objc func study() {
print("\(name)이 공부한다.")
}
}
class CollegeStudent: Student {
override func study() {
print("\(name)이 대학 공부를 한다.")
}
}
let student = CollegeStudent(name: "철수")
student.study()
- 확장에서 구현된 메서드는 기본적으로 재정의할 수 없다
- 하지만 메서드 정의에
@objc를 추가하면 재정의가 가능해진다 (Objective-C 런타임과 호환되기 때문)
- 이 방식은 주로 클래스 타입에서만 사용된다 (구조체나 열거형에서는 불가능)
확장에서 추가 가능한 멤버의 종류
- (타입) 계산 속성, (인스턴스) 계산 속성
- (타입) 메서드, (인스턴스) 메서드
- 새로운 생성자 (클래스의 경우, 편의생성자만 추가 가능)
- 서브스크립트 (
subscript)
- 새로운 중첩 타입 (
enum, struct 등)
- 프로토콜 채택 및 관련 메서드 구현
지정생성자 및 확장에서의 제약사항
- 확장에서는 지정생성자와 소멸자를 정의할 수 없다
- 지정생성자는 반드시 본체에서 정의해야 하며, 확장에서 정의하면 컴파일 에러가 발생한다
- 확장에서는 오직 편의생성자만 추가할 수 있다 (클래스의 초기화를 간편하게 만들어주는 생성자)
요약
- 확장은 기존 타입을 수정하지 않고도 기능을 추가할 수 있는 수평적인 개념이다
- 기존 타입에 새로운 기능(메서드, 계산 속성 등)을 추가할 수 있지만, 기존 메서드를 재정의할 수 없다
- 상속은 기존 타입을 기반으로 새로운 타입을 만들고 기존 기능을 재정의(Override)할 수 있는 수직적인 개념이다
- 기본 타입 (
Int, String, 등)도 확장을 통해 기능을 추가할 수 있다 (소급 모델링)
- 지정생성자 및 소멸자는 확장에서 정의할 수 없고, 저장 속성도 추가할 수 없다
- 확장에서 구현된 메서드를 재정의하려면
@objc를 사용해야 한다