2017-09-16 20 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

第1パラメータは、呼び出しに対する汎用パラメータの特殊化に使用されます。スーパークラスのメタタイプの変数にサブクラスメタタイプのインスタンスを渡して渡すのはちょっと奇妙です。ここで行う必要のある具体的なことがありますが、 'Lion.self'を直接渡すことはできません。 –

+0

しかし、ジェネリックパラメータは、呼び出し元が戻り値で行うことから推測できますか? (もちろん、この例では 'Lion.self'を渡すことができますが、実行時に決定されたさまざまなサブクラスをデコードすることができます。) – andyvn22

+0

いいえ、常にそうとは限りません - 戻り値の型追跡が困難なあいまいさにつながる可能性があります。そのあいまいさを防ぐための唯一の保証された方法は、メタタイプの引数を渡すことです。コードで汎用引数の代わりに渡される具象メタタイプを使用することは可能ですが、これはかなりユニークな使用例です。ただし、実行時に型を切り替え、正しい静的型でコールをデコードすることは可能です。 –

答えて

1

decode(...)コールのメタタイプパラメータは、汎用パラメータの特殊化に使用されます。 Swiftには、C++のようなジェネリックスを手動で専門化する構文はありません(例:decode<Int>(forKey: ...))。これはジェネリックパラメータを具体的なタイプにバインドする方法です。

戻り値の型に依存する代わりにメタタイプを渡すことの利点は、式の結果が明白であることです。リターン結果に頼ることは、いくつかの意外な状況につながることができます:明示的なメタタイプで

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 { 
    ^

protocol DefaultInitializable { 
    init() 
} 

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

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

foo(defaultValue()) 

結果、これは非問題です。

渡すメタタイプの具体的なインスタンスに対して汎用タイプが使用される理由は、コンクリートメタタイプインスタンスにはそれ自身とは異なる静的タイプがあることは一般的に予想外です。

関連する問題