제네릭 코드는 정의한 요구사항에 따라서 모든 타입에서 동작할 수 잇는 유연하고 재사용가능한 함수와 타입을 작성 가능하다. 예를 들어서 배열과 딕셔너리 타입은 둘다 제네릭 콜렉션이다. 따라서 Int, String, 등 다양한 타입을 모두 소화 가능하며 별도의 제한이 없다.
func swapTwoInts(_ a : inout Int, _ b : inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
//다음의 함수는 서로의 값을 바꾸는 함수로서 2개의 인트타입 파라미터에 대해서 호출이 가능하다.
//하지만 만약 String에도 적용하고 싶은 경우에는 String용 함수를 다시 만들어야 한다.
func swapTwoInts(_ a : inout String, _ b : inout String) {
let temporaryA = a
a = b
b = temporaryA
}
//다음과 같이 직접 같은 함수를 파라미터 타입만 변경하여 다시 정의해야 한다.
func swapTwoValues<T>(_ a : inout T, _ b : inout T) {
let temporaryA = a
a = b
b = temporaryA
}
// 다음의 함수는 제네릭 버전의 같은 함수형태이다.
func swapTwoValues<T>(_ a : inout T, _b : inout T)
func swapTwoValues(_ a : inout Int, _ b : inout Int)
//위가 제네릭, 아래가 일반 Int타입의 함수형이다.
//제네릭 함수는 앞에 <>안에 타입을 명시해주고 사용한다.
var someInt = 3
var anotherInt = 100
swapTwoValue(&someInt, &anotherInt)
//다음의 경우 타입을 자동으로 Int로 인식하고 함수를 호출한다.
struct IntStack {
var items : [Int] = []
mutating func push(_ item : Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
//다음의 구조체는 구조체 값을 변경하기 위헤 메서드 앞에 mutating 키워드를 붙여주고 있다.
//그러나 타입을 Int타입만 명시해 둔 상태이므로 다른 타입 사용이 불가하다.
struct Stack<Element> {
var items : [Element] = []
mutating func push(_ item : Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
//다음의 구조체는 위와 동일하나 제네릭을 사용해서 타입의 제한을 없앤 것이다.
//Element라는 타입의 임의 이름은 구조체 안에서 어디서나 참조된다.
var stackOfString = Stack<String>()
stackOfStrings.push("uno")
//제네릭의 구체적인 타입을 지정해서 인스턴스를 생성할 수 있다.
extension Stack {
var topItem : Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
//다음 예시처럼 단순한 타입에 대한 확장을 간단히 명시할 수 있다.
//이 확장은 타입 파라미터 목록을 정의하지 않는다.
//타입 파라미터 목록을 정의하지 않으나, Element라는 타입의 이미 존재하는 타입 파라미터 이름은
//확장 내에서 정상적으로 사용된다.