2014-12-23 1 views
6

기본 클래스 또는 프로토콜을 사용하는 것 이상의 제네릭 사용의 이점을 이해할 수있는 사람이 있습니까? 아마도 난 그냥 스위프트가 더 몇 번을 안내 읽을 필요가 있지만, 제네릭의 개념은 단지에 침몰되지 않습니다. 제네릭신속한 제네릭과 프로토콜 또는 기본 유형으로 매개 변수 처리 비교

를 사용하여이 예를 고려
func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) 
{ 
    var index = find(array, object) 
    array.removeAtIndex(index!) 
} 

가 왜 그냥 이런 식으로 쓰기?

// As pointed out, this does not compile. I was more-so curious as to why 
func removeObject(object: Equatable, inout fromArray array: [Equatable]) 
{ 
    var index = find(array, object) 
    array.removeAtIndex(index!) 
} 

설명해 주셔서 감사합니다.


업데이트. 예, 명확하게 설명하기 위해 예는 전적으로 가설적인 내용이었습니다. 나는 Objective-C에서 어떻게 할 것인지에 관해서 문제를 생각하고있었습니다. 그 점에서 나는 단지 id 타입의 매개 변수를 전달할 것이고, 그렇게 할 것입니다.

내 질문은 Swift에서 왜 비슷한 패턴이 허용되지 않는지에 대한 통찰력을 얻기위한 것이 었습니다. 대신 제네릭이 대신 사용되는 이유에 대한 통찰력을 얻었습니다.

+1

두 번째 스 니펫이 컴파일되지 않고 '프로토콜 Equatable은 일반 제약 조건으로 만 사용할 수 있습니다.'컴파일러 오류가 발생합니다. 음, 컴파일 할 수 있다는 것은 상당한 이점입니다, 그렇죠? ;) – siejkowski

+0

저는 이것이 깊은 문제에 관한 훌륭한 질문이라고 생각합니다. 예, 두 번째 스 니펫은 컴파일되지 않습니다. 왜 안돼? 왜 컴파일러가 컴파일 할 수 없습니까? 왜 그 언어는 그렇게 작동합니까? – algal

답변

5

프로토콜의 경우 프로토콜 자체에 따라 다릅니다. 프로토콜이 Self 또는 typealias을 사용하면 직접 사용할 수 없습니다. 다른 프로토콜의 경우 protocol<MyProtocol> 변수 및 매개 변수를 선언 할 수 있습니다 (예 : var o: protocol<MyProtocol>). Equatable 프로토콜을 만족해야이 (이 경우 Self에) 선언 특정 제약하는 방식으로 설계되어, 따라서 그것은 단지 제네릭 형식 제약 조건으로 사용할 수 있기 때문에 당신이 var o: protocol<Equatable> 말할 수 없다

이유입니다. 즉 컴파일러에서을 컴파일 할 때 을 알아낼 수 있어야하고 Equatable 인 모든 것에 대해서는 이 있으며 그 중 하나는 var o: protocol<Equatable>에 (항상) 수행 할 수 없습니다.

왜 프로토콜이나 기본 클래스 대신 제네릭을 사용합니까? 왜냐하면 제네릭은 여전히 ​​유형 안전성을 유지하면서 제네릭보다 더 일반화 될 수 있기 때문입니다. 이것은, 예를 들어, 콜백과 같은 것으로 특히 유용합니다. 여기에 매우 인위적인 예입니다 :

class Useless<T> { 
    private let o: T 
    private let callback: (T, String) -> Void 
    required init(o: T, callback: (T, String) -> Void) { 
     self.o = o 
     self.callback = callback 
    } 
    func publish(message: String) { 
     callback(o, message) 
    } 
} 

var useless = Useless(o: myObject) { obj, message in 
    // Here in the callback I get type safety. 
    obj.someMethod(message) 
} 

(..이 코드는 어느 누구에 의해 실행 된 적이 그것은 의사 코드로 간주한다)

지금,이 여러 가지 이유로 매우 어리석은 예입니다, 그러나 요점을 설명합니다. 제네릭 덕택에 obj 콜백 매개 변수는 전적으로 유형에 안전합니다. 콜백에서 어떤 코드가 호출 될지 예상 할 수 없기 때문에 기본 클래스 나 프로토콜을 사용하여이 작업을 수행 할 수 없습니다. Useless 클래스는 으로 유형을 사용할 수 있습니다.

0

예를 들어 실제로는 좋지 않습니다. Equatable 프로토콜에 Self에 대한 참조가 있으며 매개 변수 유형으로 사용할 수 없으므로 두 번째 예제는 컴파일되지 않습니다.

다른 주요 이점은 각 일반 식별자가 해당 유형의 모든 매개 변수를 동일한 유형으로 제한한다는 것입니다. 따라서 첫 번째 예에서는 objectfromArray이 같은 유형이어야하며 두 번째 예에서는 그렇지 않습니다.

6

Swift 팀이 dev 포럼에 게시하여 두 번째 예제를 첫 번째 줄임말로 사용할 수있는 것이 좋을 것이라고 생각합니다.그러나, 나는 그것이 여전히 정말로 일반적인 함수라고 생각한다. 하나를 선언하는 것의 단축형인가?

어떻게 다릅니 까? 다른 대답이 지적한대로 Equatable은 일반적으로 만 사용할 수 있습니다. 그러나 꼭 그렇게 할 필요는없는 예를 들어 봅시다. 방법이 있습니다 :이에서

func f<T: Printable>(t: T) { 
    // do some stuff 
} 

다른

:

func g(p: Printable) { 
    // do some stuff 
} 

의 차이는, f가 무엇 이건, 컴파일시에 생성되는 함수의 가족을 정의 t로 전달되는 유형 * Int을 전달하면 마치 func f(t: Int) { … }이라고 쓰여진 것처럼 보입니다. 당신이 Double 전달하는 경우, 그것은 *이 과도하게 단순화 func f(t: Double) { … }

를 작성하는 것과 같다 그러나 다른 한편으로는 ... 지금은 그것으로

를 이동 g는 하나의 함수는은이며, 런타임은 Printable 프로토콜에 대한 참조 만 허용 할 수 있습니다.

실제로 차이는 거의 감지 할 수 없습니다. 다른 기능 f 내부의 t를 전달하는 경우 예를 들어,이 같은 역할 :

그래서 예를 들면
func f(i: Int) { 
    // h doesn’t receive an Int 
    // but a Printable: 
    h(i as Printable) 
} 

:

func f<T: Printable>(t: T) { 
    println(sizeof(t)) 
} 

f(1 as Int8) // prints 1 
f(1 as Int64) // prints 8 
: 당신은 비록 작은 방법의 차이를 볼 수 있습니다

func h(i: Int) { 
    println("An Int!") 
} 

func h(p: Printable) { 
    println("A Printable!") 
} 

func f<T: Printable>(t: T) { 
    h(t) 
} 

h(1) // prints "An Int!" 
f(1) // prints "A Printable!" 

가장 큰 차이점은 프로토콜이 아닌 실제 제네릭 형식을 반환 할 수 있다는 것입니다.

func f<T: Printable>(t: T) -> T { 
    return t 
} 

func g(p: Printable) -> Printable { 
    return p 
} 

let a = f(1) // a is an Int 
let b = f([1]) // b is an [Int] 

let c = g(1) // c is a Printable 
let d = g([1]) // d is a Printable 

이 마지막 예제는 관련 유형이있는 프로토콜을 일반적으로 만 사용할 수있는 이유를 이해하는 데 핵심적인 역할을합니다. 당신이 first 당신 자신의 구현 만들고 싶어한다고 가정

func first<C: CollectionType>(x: C) -> C.Generator.Element? { 
    if x.startIndex != x.endIndex { 
     return x[x.startIndex] 
    } 
    else { 
     return nil 
    } 
} 

first는 일반적인 기능 및 CollectionType 프로토콜의 인수를받은 단지 일반 기능 아니었다면, 어떻게 무엇을 변화 할 수있을 것입니다 그것을 돌아왔다?