/*--------------Ex 1.--------------*/
// 정수 2개를 swap하는 함수의 정의
var num1 = 10
var num2 = 20
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tmpA = a
a = b
b = tmpA
}
swapTwoInts(&num1, &num2)
// Double을 swap하려면 함수를 새로 정의해야 한다
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let tempA = a
a = b
b = tempA
}
// 마찬가지로 문자열을 swap하려면 또 새로 함수를 구현해야 한다 -> 불편함을 느낀다!
/*--------------Ex 2.--------------*/
// 배열의 원소들을 출력하는 함수
let numbers = [2, 3, 4, 5]
let scores = [3.0, 3.3, 2.4, 4.0, 3.5]
let people = ["Jobs", "Cook", "Musk"]
// 정수 배열의 출력
func printIntArray(array: [Int]) {
for number in array {
print(number)
}
}
// Double 배열의 출력
func printDoubleArray(array: [Double]) {
for number in array {
print(number)
}
}
// 마찬가지로 불편함이 느껴진다!! -> 제네릭으로 통합 가능
<T>
는 함수 내부에서 파라미터의 타입이나 리턴형으로 사용<T, U>
이런 식으로 사용 가능func swapTwoValues<T>(_ a: inout T, _ b: inout T) -> T { // 리턴 타입으로 사용 가능
let tmpA = a
a = b
b = tmpA
return a // 그냥 리턴 타입으로 쓸 수도 있는거 보여주려고
}
func printArray<T>(array: [T]) {
for element in array {
print(element)
}
}
// 배열
let arr: Array<String> = ["a", "b"]
// 딕셔너리
let dict: Dictionary<String, Int> = ["Alex": 25, "Michel": 18]
// 옵셔널
let opt: Optional<String>
// swap 함수
// swap(a: &T, b: &T)
class, struct, enum 타입 이름 뒤에 <T>
를 추가하면, 제네릭 타입으로 선언된다
속성의 자료형, 메서드의 파라미터형식, 리턴형을 타입 파라미터로 대체할 수 있다
/*-----------------struct-----------------*/
struct GenericMem<T> {
var members: [T] = []
}
var member1 = GenericMember(members: ["Jobs", "Cook", "Musk"])
var member2 = GenericMember(members: [1, 2, 3])
// 주의할 점 : 이렇게 한 번 정의하면 타입이 고정이 된다
// (한 번 선언하면 메모리 구조가 결정이 된다!)
// member1 = GenericMember(members: [1, 2, 3]) // 에러 발생
/*-----------------class-----------------*/
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-----------------*/
// 열거형에서는 구체적인 연관값을 가질 때만 제네릭으로 정의할 수 있다
// 어차피 케이스는 선택 항목중에 하나일 뿐이기 때문에, 그걸 타입으로 정의할 일은 없다
enum Pet<T> {
case dog
case cat
case etc(T)
}
let animal1 = Pet.etc("고슴도치")
let animal2 = Pet.etc(30)
struct Coordinates<T> {
var x: T
var y: T
}
/*Ex 1*/
extension Coordinates { // Coordinates<T> 라고 쓰지 않는다!
// 튜플로 리턴하는 메서드
func getPlace() -> (T, T) {
return (x, y)
}
}
let place = Coordinates(x: 5, y: 5)
print(place.getPlace())
/*Ex 2*/
// where 절 추가 가능
// 타입이 Int형 일 때만 메서드가 적용!!
extension Coordinates where T == Int {
func getIntArray() -> [T] {
return [x, y]
}
}
let place2 = Coordinates(x: 3, y: 5)
place2.getIntArray() // 정수형 일 때만 메서드가 나타난다!
/*----------------프로토콜 제약----------------*/
// <T: Equatable>
// : Equatable이라는 프로토콜을 채택한 타입만 타입으로서 이 함수에서 사용이 가능하다!
// (Equatable 프로토콜 : == 이라는 메서드를 반드시 구현해야 하는 프로토콜 - 나중에 배운다)
func findIndex<T: Equatable>(item: T, array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if item == value {
return index
}
}
return nil
}
/*----------------클래스 제약----------------*/
class Person {}
class Student: Person {}
let person = Person()
let student = Student()
// Person의 상속 구조 상에 있는 타입만 사용 가능하다!
// 해당 타입을 상속한 클래스는 가능
func personClassOnly<T: Person>(array: [T]) {
}
personClassOnly(array: [person, person])
personClassOnly(array: [student, student])
personClassOnly(array: [Person(), Student()]) // 바로 생성해서 넣어주기
<T>
(x) associatedtype T
(o)protocol RemoteControl {
associatedtype T
func changeChannel(to: T)
func alert() -> T?
}
struct TV: RemoteControl {
// 명시적으로 어떤 타입을 사용할 지 써주기
typealiase T = Int // 생략 가능
func changeChannel(to: Int) {
print("TV 채널바꿈: \(to)")
}
func alert() -> Int? {
return 1
}
}
class Aircon: RemoteControl {
func changeChannel(to: String) {
print("Aircon 온도바꿈: \(to)")
}
func alert() -> String? {
return "1"
}
}
protocol RemoteControl2 {
// <T: Equatable> 제약 추가
associatedtype Element: Equatable
func changeChannel(to: Element)
func alert() -> Element?
}