let startRadians = startAngle * .pi / 180.0
let endRadians = endAngle * .pi / 180.0
func radians(from degrees: Double) -> Double {
degrees * .pi / 180.0
}
let startRadians = radians(from: startAngle)
let endRadians = radians(from: endAngle)
<T> where T: Idea
let number: Int = 3
##WWDC영상에서 제공하는 코드 : 농장 시뮬레이션을 위한 코드

struct Cow {
func eat(_ food: Hay) {}
}
struct Hay {
static func grow() -> Alfalfa {
return Alfalfa()
}
}
struct Alfalfa {
func harvest() -> Hay {
Hay()
}
}
struct Farm {
func feed(_ animal: Cow) {
let alfalfa = Hay.grow()
let hay = alfalfa.harvest()
animal.eat(hay)
}
}
농장(Farm)이 있다고 생각해보자. 농장엔 소(Cow)가 있고, 소에게 먹일 곡물(Hay), 그리고 곡물을 수확할 식물(Alfalfa)이 있다.
feed - 식물(Alfalfa)에게서 곡물(Hay)을 수확하고, 수확한 곡물을 동물인 소(Cow)에게 먹임으로써 우리는 농장을 운영할 수 있다.
그러나 소말고도 아래와 같이 말, 닭과 같은 동물을 농장에서 추가적으로 기르려고 하면 어떻게 할까?
struct Cow {
func eat(_ food: Hay) {}
}
struct Horse {
func eat(_ food: Carrot) {}
}
struct Chicken {
func eat(_ food: Grain) {}
}
struct Farm {
func feed(_ animal: Cow) {
let alfalfa = Hay.grow()
let hay = alfalfa.harvest()
animal.eat(hay)
}
func feed(_ animal: Horse) {
let root = Carrot.grow()
let carrot = root.harvest()
animal.eat(carrot)
}
func feed(_ animal: Chicken) {
let wheat = Grain.grow()
let grain = wheat.harvest()
animal.eat(grain)
}
}
말과 닭, 소와 같은 동물 타입의 구조체들을 선언했을 때, 동물들은 모두 어떤 음식을 먹을 수 있는 eat 메소드를 가지고 있는 것을 확인할 수 있었다.
각 동물들은 서로 다른 음식을 먹고, 또한 음식을 먹는 방법 또한 다를 것이다.
우리는 여기서 eat 메소드를 abstract code 로 작성할 수 있고, eat 메소드가 정의된 concrete type 에 따라 다르게 동작하도록 정의할 수 있다.
abstract code 가 서로 다른 concrete type 에서 다르게 동작하는 것을 polymorphism (다형성) 이라고 한다.
다형성의 다양한 형태
1. function overloading(함수 오버로딩)
class Animal {
func eat(_ food: ???) {fatalError("Subclass must implement `eat`")}
}
위의 코드는 Animal이라는 클래스가 eat이라는 함수를 가지고있고 이를 상속받는 함수는 이 함수를 override할수있게된다 하지만 상속받는 클래스의 특징에 따라 food가 다르기 때문에 이부분을 해결해야한다는 문제를 가지고있다, 여기서는 구체적인 타입이 될수가 없구나 하는정도만 이해하고 우선은 이대로 넘어간다
그 다음 구조체로 선언된 각 동물들을 클래스 타입으로 바꾸고, Animal 슈퍼클래스를 상속시키자
Animal 슈퍼클래스를 상속한 동물 클래스들에서 eat 메소드를 재정의하자.
class Cow: Animal {
override func eat(_ food: Hay) {}
}
class Horse: Animal {
override func eat(_ food: Carrot) {}
}
class Chicken: Animal {
override func eat(_ food: Grain) {}
}
각 동물은 서로 다른 타입의 음식을 먹는다. 이것은 클래스 계층 구조로 표현하기 정말 어렵다
인스턴스 간 상태를 공유하고 싶지 않아도 강제로 Reference 타입인 class 타입을 사용해야 한다.
super class 의 method 를 재정의하지 않으면, 런타임 이전까지 에러를 알지 못한다. 왜냐면 class의 함수는 direct dispatch방식이 아니라 table dispatch방식이기때문에 런타임이 되서야 함수를 알 수 있게된다. 컴파일시점에서는 알수없기때문에 문제가 발생할 수 있다
각 동물이 서로 다른 타입의 음식을 먹는 것을 어떻게 표현할 수 있을까?
class Animal {
func eat(_ food: Any) {fatalError("Subclass must implement `eat`")}
}
class Cow: Animal {
override func eat(_ food: Any) {
guard let food = food as? Hay else { fatalError("Cow cannot eat \(food)") }
}
}
class Horse: Animal {
override func eat(_ food: Any) {
guard let food = food as? Carrot else { fatalError("Horse cannot eat \(food)") }
}
}
class Chicken: Animal {
override func eat(_ food: Any) {
guard let food = food as? Grain else { fatalError("Chicken cannot eat \(food)") }
}
}
class Animal<Food> {
func eat(_ food: Food) {fatalError("Subclass must implement `eat`")}
}
class Cow: Animal<Hay> {
override func eat(_ food: Hay) {
...
}
}
class Horse: Animal<Carrot> {
override func eat(_ food: Carrot) {
...
}
}
class Chicken: Animal<Grain> {
override func eat(_ food: Grain) {
...
}
}
struct Hay {
static func grow() -> Alfalfa {
return Alfalfa()
}
}
class Animal<Food, Habitat, Commodity>
protocol Animal {
associatedtype Feed: AnimalFeed
func eat(_ food: Feed)
}
associatedtype 은 type parameter 와 마찬가지로 concrete type 을 위한 플레이스홀더 역할을 한다.
associatedtype 은 protocol 을 conform 하는 타입에 의존적이다. (무슨 말인지 모르겠다면 아래에서 추가적인 예시를 보도록 하자)
eat 메소드는 associatedtype 으로 정의된 Feed 타입을 받는 메소드
protoocl 은 위와 같이 청사진만 제공해줄 뿐 실제 구현을 하진 않는다.
protocol Animal {
associatedtype Feed: AnimalFeed
func eat(_ food: Feed)
}
struct Cow: Animal {
func eat(_ food: Hay) { ... }
}
struct Horse: Animal {
func eat(_ food: Carrot) { ... }
}
struct Chicken: Animal {
func eat(_ food: Grain) { ... }
}
protocol Animal {
associatedtype Feed: AnimalFeed
func eat(_ food: Feed)
}
struct Farm {
func feed(_ animal: ???) {...}
}
struct Farm {
func feed<A>(_ animal: A) where A: Animal {...}
// or
func feed<A: Animal>(_ animal:A) {...}
}
func feed(_ animal: some Animal)
func feed(_ animal: some Animal)
func feed<A: Animal>(_ animal: A)
func feed(_ animal: some Animal)
func getValue(Parameter) -> Result
opaque type 은 input 과 output 모두에 사용될 수 있다.
func getValue(Parameter) 같은 함수에서 타입 파라미터는 모두 input side 에 작성되는데, 따라서 함수를 호출하는 쪽에서 underlying type 을 결정한다.
보통 값을 넣어주는 쪽이 underlying type 을 결정하고, 값을 사용하는 쪽이 opaque type 과 같은 abstract type 을 바라본다.
let animal: some Animal = Horse()
var animal: some Animal = Horse()
animal = Cow() // Compiler Error 발생! underlying type 이 이미 Horse 로 결정된 상태에서 Cow 로 바꾸려고 시도하기 때문이다.
struct Silo<Material> {
private var storage: [Material]
init(storing materials: [Material]) {
self.storage = materials
}
}
var hayStorage: Silo<Hay>
protocol Animal {
associatedtype Feed: AnimalFeed // <- grow static function 을 가지고 있는 추상화 프로토콜 이라고 생각하자.
func eat(_ food: Feed)
}
struct Farm {
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow() // Animal 의 associatedtype에 접근하기 위해 type(of:) 를 사용할 수 있다.
let produce = crop.harvest() // 작물로부터 곡물을 획득한다.
animal.eat(produce) // opaque animal type 에 곡물을 먹인다.
}
}
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow()
let produce = crop.harvest()
animal.eat(Hay.grow().harvest()) // 만약 올바르지 않은 Feed 타입을 먹이려고 한다면, 컴파일러에 의해 에러가 발생한다.
}
struct Farm {
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow()
let produce = crop.harvest()
animal.eat(produce)
}
func feedAll(_ animals: [some Animal]) {
}
}

func feedAll(_ animals: [any Animal]) {
}
any 키워드는 여러 가지 종류의 Animal 타입을 배열에 담을 수 있고, underlying type 또한 런타임에 실시간으로 변경될 수 있다는 것을 알려주는 키워드이다.
any 키워드 역시 conformance requirement 의 앞에 작성한다.
any 키워드를 통해 여러가지 concrete animal type 을 저장할 수 있는데, 이것을 통해 우리는 값 타입에서 subtype polymorphism 을 구현할 수 있다.
func feedAll(_ animals: [any Animal]) {
for animal in animals {
animal.eat(food: Animal.Feed) // 이 부분에서 컴파일 에러가 발생한다!
}
}
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow()
let produce = crop.harvest()
animal.eat(produce)
}
func feedAll(_ animals: [any Animal]) {
for animal in animals {
feed(animal) // any Animal 을 some Animal 으로 넘겨서 underlying type 을 unboxing 한다.
}
}