Day 13 - 2023.01.19
※※ 익스텐션은 타입에 새로운 기능을 추가할 수 있지만, 기존에 존재하는 기능을 재정의할 수는 없다.
이 둘은 비슷해보이지만 실제 성격은 많이 다르다.
상속 | 익스텐션 | |
---|---|---|
확장 | 수직 확장 | 수평 확장 |
사용 | 클래스 타입 | 클래스, 구조체, 프로토콜, 제네릭 등 모든 타입 |
재정의 | 가능 | 불가능 |
익스텐션을 사용하는 대신 원래 타입을 정의한 소스에 기능을 추가하는 방법도 있겠지만, 외부 라이브러리나 프레임워크를 가져다 썻다면 원본 소스를 수정하지 못한다. 이처럼 외부에서 가져온 타입에 내가 원하는 기능을 추가하고자 할 때 익스텐션을 사용한다.
따로 상속 받지 않아도 되며, 구조체와 열거형에도 기능을 추가할 수 있으므로 익스텐션은 매우 편리한 기능이다.
익스텐션은 모든 타입에 적용할 수 있다. 모든 타입이라 함은 구조체, 열거형, 클래스, 프로토콜, 제네릭 타입 등을 뜻한다. 즉, 익스텐션을 통해 모든 타입에 연산 프로퍼티, 메서드, 이니셜라이저, 서브스크립트, 중첩 데이터 타입 등을 추가할 수 있다. 더불어 익스텐션은 프로토콜과 함께 사용하면 굉장히 강력한 기능을 선사한다.
extension 확장할 타입 이름 {
/* 타입에 추가될 새로운 기능 구현 */
}
extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3... {
/* 프로토콜 요구사항 구현 */
}
스위프트 라이브러리를 살펴보면 실제로 익스텐션이 굉장히 많이 사용되고 있음을 알 수 있다.
Double 타입에는 수많은 프로퍼티와 메서드, 이니셜라이저가 정의되어 있으며 수많은 프로토콜을 채택하고 있을 것이라 예상되지만, 실제로 Double 타입의 정의를 살펴보면 그 모든것이 다 정의되어 있지는 않다. 그러면 Double 타입이 채택하고 준수해야 하는 수많은 프로토콜은 어디로 갔을까? 어디에서 채택하고 어디에서 준수하도록 정의되어 있을까?
답은 익스텐션이다. 이처럼 스위프트 표준 라이브러리 타입의 기능은 대부분 익스텐션으로 구현되어 있다. Double 외에도 다른 타입들의 정의와 익스텐션을 찾아보면 더 많은 예를 볼 수 있다.
extension Int {
var isEven: Bool {
return self % 2 == 0
}
var isOdd: Bool {
return self % 2 == 1
}
}
print(1.isEven) // 출력: false
print(2.isEven) // 출력: true
print(1.isOdd) // 출력: true
print(2.isOdd) // 출력: false
var number: Int = 3
print(number.isEven) // 출력: false
print(number.isOdd) // 출력: true
number = 2
print(number.isEven) // 출력: true
print(number.isOdd) // 출력: false
extension Int {
func multiply(by n: Int) -> Int {
return self * n
}
}
print(3.multiply(by: 2)) // 출력: 6
print(4.multiply(by: 5)) // 출력: 20
number = 3
print(number.multiply(by: 2)) // 출력: 6
print(number.multiply(by: 3)) // 출력: 9
extension String {
init(int: Int) {
self = "\(int)"
}
intit(double: Double) {
self = "\(double)"
}
}
let stringFromInt: String = String(int: 100)
// "100"
let stringFromDouble: String = String(double: 100.0)
// "100.0"