21-1. 제네릭( Generic)의 개념과 타입으로서의 제네릭, 제네릭의 확장

🌈 devleeky16498·2022년 4월 22일
0

제네릭 코드는 정의한 요구사항에 따라서 모든 타입에서 동작할 수 잇는 유연하고 재사용가능한 함수와 타입을 작성 가능하다. 예를 들어서 배열과 딕셔너리 타입은 둘다 제네릭 콜렉션이다. 따라서 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
}
//다음과 같이 직접 같은 함수를 파라미터 타입만 변경하여 다시 정의해야 한다.
  1. 다음과 같은 경우 제네릭을 사용하면 모든 타입과 함께 동작이 가능하다.다음예시는 위의 함수를 제네릭을 통해서 손쉽게 하나의 타입으로 명시한 것이다.
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타입의 함수형이다.
//제네릭 함수는 앞에 <>안에 타입을 명시해주고 사용한다.
  1. 함수의 제네릭 버전은 Int, String등의 실제 타입 대신 다음의 경우 T라는 임의 타입을 사용했다. 이 타입은 T가 무엇이어야 하는지 말하지 않으나 a와 b는 모두 같은 타입을 의미한다. 또한 해당 제네릭은 파라미터에 따라서 타입유추를 제공한다.
var someInt = 3
var anotherInt = 100
swapTwoValue(&someInt, &anotherInt)
//다음의 경우 타입을 자동으로 Int로 인식하고 함수를 호출한다.

타입 파라미터

  1. 위에서 언급한 임의의 타입 T는 타입 파라미터의 예이다. 타입 파라미터는 임의의 타입을 지정하고 이름을 지정하며 <>의 안에 기록 후 함수의 이름 바로 뒤에 작성된다. 콤마로 구분된 괄호안에 여러개의 타입 파라미터를 작성하여 하나 이상의 타입 파라미터 제공이 가능하다.
  2. 타입 파라미터의 이름은 관계를 나타내기 위해서 Dictionary<Key, Value> 그리고, Array에서 Element와 같이 설명이 포함된 이름도 있다. 의미있는 관계가 없는 경우는 T, U등의 단순한 단일문자를 사용하는게 일반적이다.

제네릭 타입

  1. 제네릭은 함수외에도 고유한 제네릭 타입을 정의할 수 있다. 이것은 모든 타입에서 동작 가능한 사용자 정의 클래스, 구조체, 및 열거형이다.
  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")
  //제네릭의 구체적인 타입을 지정해서 인스턴스를 생성할 수 있다.

제네릭 타입의 확장

  1. 제네릭 타입을 확장 시 확장의 정의 부분으로 타입 파라미터 목록을 제공하지 않는다. 쉽게 말하면 <>을 쓰지 않는다. 이는 기존 타입에서 정의하고 확장에서는 하지 않는다.
  extension Stack {
  	var topItem : Element? {
  		return items.isEmpty ? nil : items[items.count - 1]
  	}
  }
//다음 예시처럼 단순한 타입에 대한 확장을 간단히 명시할 수 있다.
//이 확장은 타입 파라미터 목록을 정의하지 않는다.
//타입 파라미터 목록을 정의하지 않으나, Element라는 타입의 이미 존재하는 타입 파라미터 이름은
//확장 내에서 정상적으로 사용된다.
profile
Welcome to Growing iOS developer's Blog! Enjoy!🔥

0개의 댓글