2017-09-16 48 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 { 
    ^

有了一个明确的元类型,这是一个非问题。

至于为什么泛型类型用于您传入的元类型的具体实例 - 通常意外的是让具体元类型实例具有与其自身不同的静态类型。

相关问题