Swift のジェネリック - 「ジェネリックパラメータ 'T' を推論できませんでした」質問する

Swift のジェネリック - 「ジェネリックパラメータ 'T' を推論できませんでした」質問する

メソッドからUIViewController準拠するものを返したいので、メソッド シグネチャを使用します。MyProtocol

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {

最初に理解できないのは、myMethodたとえば次のシグネチャを持つ a を返す場合MyViewController、強制的にキャストする必要があることです。

class MyViewController: UIViewController, MyProtocol

単純にはできませんreturn MyViewController()が、次のように記述する必要があります: return MyViewController() as! T- なぜこれが必要なのでしょうか?

そして2つ目は、この方法をどこで使えるかということです。単純に言うことはできません

let x = myMethod() as? UIViewController

エラーが出ると

Generic parameter 'T' could not be inferred

どうすればこのようなことを実現できるでしょうか? キャストすれば動作しますがMyViewController、もちろんそれは避けたいです。

編集: 例

class MyViewController : UIViewController, MyProtocol {
}

protocol MyProtocol {
}

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

わかりました。1 つの部分は理解できましたが、 へのキャストがなぜT必要なのでしょうか。MyViewControllerは のサブクラスでありUIViewController、プロトコルに準拠しているので、キャストは必要ないはずですよね?

ベストアンサー1

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T

この宣言は、次のような意味です。 と呼ばれる関数が存在しmyMethod、それがmyMethod何らかの値を返す。特定の Tここで、Tは のサブタイプでありUIViewController、 でもありますMyProtocol。これは、 がT実際に何の型であるかを示しているわけではなく、そのような が 1 つしかないとも示していません。 のサブクラスであり に準拠するmyMethod型が多数ある場合、 も多数存在する可能性があります。これらの型はすべて、 の新しいバージョンを作成します(実際には、 が行うアサーションに対する新しいソリューションであり、そのような関数は存在します)。UIViewControllerMyProtocolmyMethodmyMethod

これは次のものと同じではありません:

func myMethod() -> UIViewController

つまり、関数はmyMethodの任意のサブタイプを返しますUIViewController

Swift では、「UIViewController のサブクラスであり、MyProtocol のサブタイプである任意の型」を表現する方法はありません。その基準を満たす特定の型についてのみ議論できます。Swift では、クラスとプロトコルをこのように組み合わせることはできません。これは言語の現在の制限であり、深刻な設計上の問題ではありません。

特定のどれでもが問題です。myMethod宣言を満たす関数は多数あります。Tルールに準拠するプラグインはすべて候補になります。したがって、 と入力してもmyMethod()、コンパイラはどの関数を意味しているのかわかりませんT

(私はこの回答を拡張して、型理論ではなく「コードでそれをどのように行うか」という観点から提供しようとしていましたが、donnywals がすでにその優れたバージョンを公開しています。)

* 編集された質問へ *

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

T特定の型は呼び出し元によって決定されます。これは「準拠する任意の型」ではなく、「準拠する特定の具体的な型」です。呼び出したケースを考えてみましょう。

let vc: SomeOtherViewController = myMethod()

この場合、.Tはその型ではないので、キャストで行っていることは危険です。SomeOtherViewControllerMyViewControlleras!

おすすめ記事