今日色々とやり取りをしながら、Swiftでのジェネリックなメソッドについてある知見を得たのでまとめておきます。
@_ishkawa @akisutesama gistにコメントしたんですが、型自体をT.Typeとしてパラメータで渡させるのはどうですか
— Syo Ikeda / いけしょー (@ikesyo) 2015, 3月 20
以下のようなSwiftの型・ジェネリックなメソッドがある時、このままでは型指定(特殊化)が面倒になってしまいます。
class Hoge { class func genericMethod<T: Request>(callback: T -> Bool) { ... } } // 呼ぶ時 Hoge.genericMethod { (x: SomeRequest) in true }
上記のような場合、型情報のヒントが与えられないのでクロージャでパラメータの型を明示しないといけません。引数にT
のインスタンス・値を渡す場合は型推論が効くし、以下のように型自体がジェネリックな場合は初期化時に明示的に特殊化が出来るのですが。。
class Generic<T> {} let generic = Generic<String>()
このようなケースでは、メソッドのパラメータとして型自体を渡させるようにするとそれが型のヒントとなりクロージャで型指定を行う必要がなくなります。
class Hoge { class func genericMethod<T: Request>(type: T.Type, callback: T -> Bool) { ... } } // 呼ぶ時 Hoge.genericMethod(SomeRequest.self) { x in true }
振り返ってみればObjective-CではこのようにClass
をパラメータで渡すことも多かった気がしますね。
このパターンを使うと、例えばUITableViewCell
のdequeueが以下のように出来、キャストが不要になります。
extension UITableView { func registerNibForClass<T: UITableViewCell>(type: T.Type) -> UINib { let nib = UINib(nibName: type.nibName(), bundle: nil) registerNib(nib, forCellReuseIdentifier: type.reuseIdentifier()) return nib } func dequeueCell<T: UITableViewCell>(type: T.Type, forIndexPath indexPath: NSIndexPath) -> T { return dequeueReusableCellWithIdentifier(type.reuseIdentifier(), forIndexPath: indexPath) as T } } extension UITableViewCell { class func simpleClassName() -> String { return NSStringFromClass(self).componentsSeparatedByString(".").last! } class func nibName() -> String { return simpleClassName() } class func reuseIdentifier() -> String { return simpleClassName() } } let cell = tableView.dequeueCell(HogeCell.self, forIndexPath: indexPath)
Javaとかでは出来たような気がしますが Hoge.genericMethod<SomeRequest> { x in true }
みたいにメソッドコール時に明示的に特殊化が出来ればいいんですけどね。