구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있습니다. 기능을 추가하려는 타입을 구현한 소스코드를 알지 못하거나 볼 수 없다고 해도, 타입만 안다면 그 타입의 기능을 확장할 수도 있습니다.
익스텐션으로 타입에 추가할 수 있는 기능
익스텐션은 타입에 새로운 기능을 추가할 수는 있지만, 기존에 존재하는 기능을 재정의할 수는 없습니다.
클래스의 상속과 익스텐션
💡 익스텐션은 언제 유용할까?
다양한 경우에서 유용하지만 가장 필요한 경우는 외부 라이브러리를 사용해서 원본 소스를 수정하지 못할 경우 추가 기능을 사용하고자 할 때 유용하게 사용됩니다.
extension 확장될 타입 이름 {
타입에 추가될 새로운 기능 구현
}
익스텐션은 기존에 존재하는 타입에 다른 프로토콜을 추가로 채택할 수 있도록 확장할 수 있습니다.
extension 확장될 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3 {
프로토콜 요구 사항 구현
}
extension Int {
var isEven: Bool {
return self % 2 == 0
}
var isOdd: Bool {
return self % 2 == 1
}
}
익스텐션으로 연산 프로퍼티를 추가할 수 있지만, 저장 프로퍼티는 추가할 수 없습니다.
또, 타입에 저장되어 있는 기존의 프로퍼티에 프로퍼티 감시자를 추가할 수도 없습니다.
extension Int {
func multiplier(by n: Int) -> Int {
return self * n
}
// 타입 메서드
static func isIntInstance(_ instance: Any) -> Bool {
return instance is Self
}
}
let result = 3.multiplier(by: 4)
print(result)
// 12
print(Int.isIntInstance(4.6))
// false
struct Position {
var x: Int
var y: Int
}
extension Position {
// 중위 연산자
static func + (left: Position, right: Position) -> Position {
return Position(x: left.x + right.x, y: left.y + right.y)
}
// 전위 연산자
static prefix func - (current: Position) -> Position {
return Position(x: -current.x, y: -current.y)
}
// 복합 할당 연산자
static func += (left: inout Position, right: Position) {
left = left + right
}
// 비교 연산자
static func == (left: Position, right: Position) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
static func != (left: Position, right: Position) -> Bool {
return !(left == right)
}
}
익스텐션 블록을 나누는 갯수의 제한은 없습니다. 그렇기 때문에 관련 기능별로 하나의 익스텐션 블록에 묶어주는 것도 좋습니다.
값 타입(구조체나 열거형 등)의 인스턴스를 초기화할 때, 타입의 정의 부분에 이니셜라이저를 추가하지 않더라도 익스텐션을 통해 이니셜라이저를 추가할 수 있습니다.
struct Position {
var x: Int
var y: Int
}
extension Position {
init() {
self.x = 0
self.y = 0
}
init(x: Int) {
self.x = x
self.y = 0
}
}
let initPosition = Position()
let xPosition = Position(x: 3)
print(initPosition)
print(xPosition)
// Position(x: 0, y: 0)
// Position(x: 3, y: 0)
하지만 클래스 타입의 경우 익스텐션으로 지정 이니셜라이저와 디이니셜라이저를 추가할 수 없습니다. 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 타입의 구현부에 위치해야 합니다. 다만 익스텐션에 편의 이니셜라이저는 추가가 가능합니다.
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
extension Person {
convenience init(age: Int) {
self.init(name: "이승철", age: 0)
self.age = age
}
}
let 이승철 = Person(age: 39)
print(이승철.age)
// 39
익스텐션을 통해 추가하는 이니셜라이저는 타입의 기존 이니셜라이저가 갖는 책무를 동일하게 수행해야 합니다. 즉, 이니셜러아저 호출이 종료되는 시점까지 인스턴스가 정상적으로 완벽하게 초기화되는 것을 책임져야 합니다.
extension String {
subscript(appendValue: Self) -> Self {
return self + appendValue
}
subscript(repeatCount: Int) -> Self {
var str = String()
for _ in 0..<repeatCount {
str += self
}
return str
}
}
print("가나다"[4]) // 가나다가나다가나다가나다
print("마바사"["아자"]) // 마바사아자
익스텐션을 통해 타입에 중첩 데이터 타입을 추가할 수 있습니다.
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
print(100.kind)
print(0.kind)
print((-4589).kind)
익스텐션을 통해 Int 타입에 Kind라는 열거형 타입과 Kind 타입의 연산 프로퍼티를 추가해주었습니다.
자료 출처: 야곰 스위프트 프로그래밍 3판
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."