Swift - 제너릭(Generics)
제너릭(Generic, 본래 발음은 ‘저너릭'에 가깝지만…)은 쉽게 설명하기 어려운 용어 같다. C++의 STL등의 템플릿을 체험해 봤다면 특정 부타입을 명시해서 내부에서 쓸 데이터의 타입을 정의하는 것을 종종 볼 수 있는데 이 제너릭은 그런 방식을 정의하기 위해 사용하는 것과 비슷하다.
이번에는 이 제너릭에 대해 얉게 파고들어 보자. (깊게 안들어간다 -_-)
제너릭의 실제 사용예는 배열(Array)이나 사전(Dictionary)형 정의 시 볼 수 있다.
제너릭 구현
일반 함수에도 제너릭을 붙일 수 있지만 요즘처럼 OOP시대에서 함수를 쓰기보다는 클래스나 구조체로 설계하는 것이 일반적이니 구조체 형식으로 보는게 좋을 것 같다. 아래 예제는 (공식 문서에는 스택을 구현하길래 달라보이려고) 자료구조 중 큐(Queue)를 구현하는 예제다.
실제로 테스트 해 보면 잘 동작한다.
비교가능화? Equatable
위 예제에 메소드를 하나 추가했다. 푸쉬한 데이터 중 첫번째와 두번째가 동일한지를 확인하기 위한 메소드다. 생뚱맞은 예만 자꾸 나오는것 같아서 참 내가 한심하다. :-)
이 경우 사용 할 수 있는 키워드가 equatable 이다. ‘동일시 할 수 있는’ 이라는 의미겠지만 개인적으로 ‘비교가능화’ 라고 표현한다.
공식 레퍼런스 문서에 나오는 내용을 보면 이 외에도 관련내용이 더 있다. 예를 들어 제너릭에 넣을 수 있는 타입을 제한(Type Constraints) 라던가 연관 타입(Associated Types)이라던가 많은 내용이 있지만 중요한 것은 위의 내용이 전부인 것 같아 생략한다. 필요하다면 직접 찾아보자. -_-;
개인적으로 제너릭은 자주 쓸 만한 개념이 아닌 것 같다. 대부분의 경우 배열형(Array)과 사전형(Dictionary)을 이용하면 해결이 된다. 아무래도 수학이나 데이터가공 분야가 아니면 잘 쓰이진 않을 것 같다.
[관련글] Swift - 구조체(Structure) 훑어보기
[관련글] Swift - 클래스(Class) 훑어보기
[관련글] Swift - 오퍼레이터 오버로드(Operator Overloads)
[돌아가기] 스위프트(Swift) 가이드
이번에는 이 제너릭에 대해 얉게 파고들어 보자. (깊게 안들어간다 -_-)
제너릭의 실제 사용예는 배열(Array)이나 사전(Dictionary)형 정의 시 볼 수 있다.
// Int형의 10개짜리 배열 생성 var array = Array<Int>(count: 10, repeatedValue: 0) // String타입의 키와 Int 타입의 데이터를 가질 수 있는 사전형 생성 var dict = Dictionary<String, Int>()이런 식으로 컨테이너 타입에 넣기 위한 아이템의 타입을 정의할 수 있는데 여기서 보이는 꺽쇠(<, >)로 정의하는 부분을 구현하려면 이 제너릭을 사용해야 한다.
제너릭 구현
일반 함수에도 제너릭을 붙일 수 있지만 요즘처럼 OOP시대에서 함수를 쓰기보다는 클래스나 구조체로 설계하는 것이 일반적이니 구조체 형식으로 보는게 좋을 것 같다. 아래 예제는 (공식 문서에는 스택을 구현하길래 달라보이려고) 자료구조 중 큐(Queue)를 구현하는 예제다.
struct MyQueue<T> { // T 타입의 배열(Array) 생성 var items = T[]() mutating func push(item: T) { self.items.append(item) } mutating func pop() -> T? { if self.items.count <= 0 { return nil } let item = self.items[0] self.items.removeAtIndex(0) return item } }구조체 선언에서 이름 옆에 <T>를 명시하는 것이 보인다. 이것이 제너릭이다. 여기서 쓰인 T라는 이름은 아무거나 막 쓸 수 있는데 아무래도 T로 쓰는게 간단하고 알아보기에도 나쁘지 않은것 같다.
실제로 테스트 해 보면 잘 동작한다.
var q = MyQueue<Int>() q.push(10) q.push(20) q.pop() // 10 q.pop() // 20 q.pop() // nil원하는대로 동작함을 알 수 있다.
비교가능화? Equatable
위 예제에 메소드를 하나 추가했다. 푸쉬한 데이터 중 첫번째와 두번째가 동일한지를 확인하기 위한 메소드다. 생뚱맞은 예만 자꾸 나오는것 같아서 참 내가 한심하다. :-)
struct MyQueue<T> { // T 타입의 배열(Array) 생성 var items = T[]() ... 생략 ... // 추가한 메소드 func isEqualFirstAndSecond() -> Bool { if self.items.count < 2 { return false } let first = self.items[0] let second = self.items[1] // 아래에서 '==' 오퍼레이터가 없다고 에러가 발생 return first == second } }실제로 에러 내용은 could not found an overload for ‘==‘ that accepts the supplied arguments 와 같은 식이다. 당연히 여기서는 제너릭으로 정해진 타입이 아닌 알 수 없는 타입을 사용하기 때문에 비교연산자가 오버로드 되어있는지 조차 알 수가 없는 것이다.
이 경우 사용 할 수 있는 키워드가 equatable 이다. ‘동일시 할 수 있는’ 이라는 의미겠지만 개인적으로 ‘비교가능화’ 라고 표현한다.
struct MyQueue<T: Equatable> { ... 생략 ... }제너릭에 Equatable 이라고 정의를 하면 이런 에러는 사라진다. 실제로 실행시켜보면 Int 타입에 한해서는 잘 동작한다고 할 수 있다.
var q = MyQueue<Int>() q.push(10) q.push(20) q.isEqualFirstAndSecond() // false q.pop() q.pop() q.push(1) q.push(1) q.isEqualFirstAndSecond() // true물론 Equatable 이 제대로 동작하기 위해서는 ‘==' 오퍼레이터가 미리 오버로드되어 있어야 가능하다. 오퍼레이터 오버로드를 참고하자.
공식 레퍼런스 문서에 나오는 내용을 보면 이 외에도 관련내용이 더 있다. 예를 들어 제너릭에 넣을 수 있는 타입을 제한(Type Constraints) 라던가 연관 타입(Associated Types)이라던가 많은 내용이 있지만 중요한 것은 위의 내용이 전부인 것 같아 생략한다. 필요하다면 직접 찾아보자. -_-;
개인적으로 제너릭은 자주 쓸 만한 개념이 아닌 것 같다. 대부분의 경우 배열형(Array)과 사전형(Dictionary)을 이용하면 해결이 된다. 아무래도 수학이나 데이터가공 분야가 아니면 잘 쓰이진 않을 것 같다.
[관련글] Swift - 구조체(Structure) 훑어보기
[관련글] Swift - 클래스(Class) 훑어보기
[관련글] Swift - 오퍼레이터 오버로드(Operator Overloads)
[돌아가기] 스위프트(Swift) 가이드
댓글