2017-09-16 82 views
3

好像decode(_forKey:)忽略了它的第一個參數,而是依賴泛型參數來決定要解碼的類型。如果是這種情況,第一個參數是什麼?爲什麼`decode(_:forKey:)`忽略它的第一個參數?

class Cat: Codable { 
    func speak() -> String { return "Meow" } 
} 

class Lion: Cat { 
    override func speak() -> String { return "Roar!" } 
} 

class Person: Codable { 
    let firstPet: Cat 
    let secondPet: Cat 
    init(firstPet: Cat, secondPet: Cat) { 
     self.firstPet = firstPet 
     self.secondPet = secondPet 
    } 

    enum CodingKeys: CodingKey { case firstPet, secondPet } 

    required init(from decoder: Decoder) throws { 
     let container = try decoder.container(keyedBy: CodingKeys.self) 
     self.firstPet = try container.decode(Lion.self, forKey: .firstPet) 
     let typeOfCat: Cat.Type = Lion.self 
     self.secondPet = try container.decode(typeOfCat, forKey: .secondPet) 
    } 
} 

let before = Person(firstPet: Lion(), secondPet: Lion()) 
let after = try! JSONDecoder().decode(Person.self, from: JSONEncoder().encode(before)) 
after.firstPet.speak() //"Roar!" 
after.secondPet.speak() //"Meow" ...really? 
+0

第一個參數用於將通用參數專門用於調用。在超類metatype的一個變量中傳遞一個子類metatype的實例並傳遞它是有點奇怪的 - 有沒有什麼具體的事情需要在這裏做,以防止你直接傳入'Lion.self'? –

+0

但是泛型參數可以從調用者用返回值做什麼來推斷,不是嗎? (是的,當然在這個例子中,我可以通過'Lion.self',但是我真正想做的是解碼在運行時確定的各種子類。) – andyvn22

+0

不,不總是 - 有些情況下依賴返回類型可能會導致難以追查的歧義;傳遞一個元類型參數是防止含糊不清的唯一有效方法。代碼可以使用傳入的具體元類型而不是泛型參數,但這是一個非常獨特的用例。不過,您應該可以在運行時切換類型,並使用正確的靜態類型調用解碼。 –

回答

1

調用decode(...)調用的元類型參數用於特化通用參數。 Swift沒有像C++那樣手動專用泛型的語法(例如decode<Int>(forKey: ...)),所以這是一種將泛型參數綁定到具體類型的方法。

傳遞元類型(而不是依賴返回類型來提供解析)的好處是表達式的結果是明確的。依託返回的結果可能會導致一些令人吃驚的情況:

protocol DefaultInitializable { 
    init() 
} 

func defaultValue<T : DefaultInitializable>() -> T { 
    return T() 
} 

func foo(_ value: Int) { 
    print(value) 
} 

foo(defaultValue()) 

結果

Untitled.swift:13:5: error: generic parameter 'T' could not be inferred 
foo(defaultValue()) 
    ^
Untitled.swift:5:6: note: in call to function 'defaultValue' 
func defaultValue<T : DefaultInitializable>() -> T { 
    ^

有了一個明確的元類型,這是一個非問題。

至於爲什麼泛型類型用於您傳入的元類型的具體實例 - 通常意外的是讓具體元類型實例具有與其自身不同的靜態類型。

相關問題