func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let tempA = a
a = b
b = tempA
}
var string1 = "hello"
var string2 = "world"
swapTwoValues(&string1, &string2)
print(string1)
print(string2)
func printArray<T>(array: [T]) {
for element in array {
print(element)
}
}
printArray(array: numbers)
printArray(array: scores)
printArray(array: people)
제네릭 타입의 정의
제네릭(Generics) 구조체 / 클래스 / (열거형)
- 클래스, 구조체, 열거형의 타입이름 뒤에 타입 파라미터를 추가하면, 제네릭 타입으로 선언됨
- 타입 파라미터는 타입(형식) 이름뒤에 선언, 타입 제약 문법도 동일함 GridPoint<T: Equatable>
- 속성의 자료형, 메서드의 파라미터형식, 리턴형을 타입 파라미터로 대체 가능
struct Member {
var members: [String] = []
}
struct GenericMember<T> {
var members: [T] = []
}
var member1 = GenericMember(members: ["Jobs", "Cook", "Musk"])
var member2 = GenericMember(members: [1, 2, 3])
print( member1.members)
print(member2.members)
class GridPoint<A> {
var x: A
var y: A
init(x: A, y: A){
self.x = x
self.y = y
}
}
let aPoint = GridPoint(x: 10, y: 20)
let bPoint = GridPoint(x: 10.4, y: 20.5)
enum Pet<T> {
case dog
case cat
case etc(T)
}
let animal = Pet.etc("고슴도치")
제네릭을 Extension(확장)에도 적용할 수 있다
extension Coordinates {
func getPlace() -> (T, T) {
return (x, y)
}
}
let place = Coordinates(x: 5, y: 5)
print(place.getPlace())
extension Coordinates where T == Int {
func getIntArray() -> [T] {
return [x, y]
}
}
let place2 = Coordinates(x: 3, y: 5)
place2.getIntArray()
타입 제약(Type Constraint)
- 제네릭에서 타입을 제약할수 있음
- 타입 매개 변수 이름 뒤에 콜론으로 "프로토콜" 제약 조건 또는 "단일 클래스"를 배치 가능
- (1) 프로토콜 제약 <T: Equatable>
- (2) 클래스 제약 <T: SomeClass>
- 특정 클래스와 상속관계에 내에 있는 클래스만 타입으로 사용할 수 있다는 제약 (구조체, 열거형은 사용 못
- Equatable 프로토콜을 채택한 타입만 해당 함수에서 사용 가능 하다는 제약
func findIndex<T: Equatable>(item: T, array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if item == value {
return index
}
}
return nil
}
findIndex(item: 7 , array: [3, 4, 5, 6, 7])
let aNumber = 5
let someArray = [3, 4, 5, 6, 7]
if let index = findIndex(item: aNumber, array: someArray) {
print("밸류값과 같은 배열의 인덱스: \(index)")
}
class Person {}
class Student: Person {}
let person = Person()
let student = Student()
func personClassOnly<T: Person>(array: [T]) {
}
personClassOnly(array: [person, person])
personClassOnly(array: [student, student])
반대로 구체/특정화(specialization) 함수구현도 가능
- 항상 제네릭을 적용시킨 함수를 실행하게만 하면, 또다른 불편함이 생기지 않을까?
- (제네릭 함수가 존재하더라도) 동일한 함수이름에 구체적인 타입을 명시하면, 해당 구체적인 타입의 함수가 실행됨
- 문자열의 경우, 대소문자를 무시하고 비교하고 싶어서 아래와 같이 구현 가능 ⭐️
- 위의 findIndex<T: Equatable>(item: T, array:[T]) -> Int? 와 완전 동일
func findIndex(item: String, array:[String]) -> Int? {
for (index, value) in array.enumerated() {
if item.caseInsensitiveCompare(value) == .orderedSame {
return index
}
}
return nil
}
let aString = "jobs"
let someStringArray = ["Jobs", "Musk"]
if let index2 = findIndex(item: aString, array: someStringArray) {
print("문자열의 비교:", index2)
}
프로토콜에서의 제네릭 문법의 사용
프로토콜에서 제네릭의 사용 - Associated Types(연관 타입)
- 연관타입(Assiciated Types)으로 선언해야함 ⭐️
- 프로토콜은 타입들이 채택할 수 있는 한차원 높은 단계에서
- 요구사항만을 선언(자격증)하는 개념이기 때문에
- 제네릭 타입과 조금 다른 개념(연관타입)을 추가적으로 도입한 것 뿐
protocol RemoteControl {
associatedtype T
func changeChannel(to: T)
func alert() -> T?
}
struct TV: RemoteControl {
typealias T = Int
func changeChannel(to: Int) {
print("TV 채널바꿈: \(to)")
}
func alert() -> Int? {
return 1
}
}
let tv = TV()
tv.changeChannel(to: 2)
tv.alert()
class Aircon: RemoteControl {
func changeChannel(to: String) {
print("Aircon 온도바꿈: \(to)")
}
func alert() -> String? {
return "1"
}
}
let air = Aircon()
air.changeChannel(to: "20")
air.alert()