メソッドから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
型が多数ある場合、 も多数存在する可能性があります。これらの型はすべて、 の新しいバージョンを作成します(実際には、 が行うアサーションに対する新しいソリューションであり、そのような関数は存在します)。UIViewController
MyProtocol
myMethod
myMethod
これは次のものと同じではありません:
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
はその型ではないので、キャストで行っていることは危険です。SomeOtherViewController
MyViewController
as!