【问题标题】:Why does `decode(_:forKey:)` ignore its first parameter?为什么 `decode(_:forKey:)` 会忽略它的第一个参数?
【发布时间】:2017-09-16 22:42:56
【问题描述】:

似乎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?

【问题讨论】:

  • 第一个参数用于将泛型参数特化为调用。在超类元类型的变量中传递子类元类型的实例并传入它有点奇怪——你需要在这里做一些具体的事情来阻止你直接传入Lion.self吗?
  • 但是泛型参数可以从调用者对返回值所做的事情中推断出来,不是吗? (是的,当然在这个例子中我可以传递Lion.self,但我真正想做的是解码在运行时确定的各种子类。)
  • 不,并非总是如此——在某些情况下,依赖返回类型会导致难以追踪的模糊性;传递元类型参数是防止这种歧义的唯一保证方法。代码可以使用传入的具体元类型而不是通用参数,但这是一个非常独特的用例。不过,您应该可以在运行时打开类型并使用正确的静态类型调用 decode。
  • 有趣——我想你的第一条评论实际上就是答案。想写出来吗? (而且,我无法切换,因为在编译时甚至不知道 possible 类型——我不得不对superDecoder() 进行一些可疑的使用,所以我可以直接调用init(from:)正确的类型。)
  • 已发表,谢谢!顺便说一句,如果您认为这是一个错误,请将其提交到 bugs.swift.org

标签: swift codable


【解决方案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 {
     ^

使用显式元类型,这不是问题。

至于为什么在你传入的元类型的具体实例上使用泛型类型——具体元类型实例具有与其自身不同的静态类型通常是出乎意料的。

【讨论】:

    猜你喜欢
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    • 2014-09-08
    相关资源
    最近更新 更多