Swiftでジェネリックプロトコルを作成するにはどうすればよいですか?


85

一般的な入力を受け取り、一般的な値を返すメソッドを使用してプロトコルを作成したいと思います。

これは私がこれまで試したことですが、構文エラーが発生します。

宣言されていない識別子Tの使用。

私は何が間違っているのですか?

protocol ApiMapperProtocol {
    func MapFromSource(T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    func MapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel() as UserModel
        var accountsData:NSArray = data["Accounts"] as NSArray     
        return user
    } 
}

私の答えを確認してください:stackoverflow.com/a/54900296/3564632
denis_lor

回答:


141

プロトコルによっては少し異なります。Appleのドキュメントの「AssociatedTypes」をご覧ください。

これがあなたの例での使い方です

protocol ApiMapperProtocol {
    associatedtype T
    associatedtype U
    func MapFromSource(_:T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    typealias T = NSDictionary
    typealias U = UserModel

    func MapFromSource(_ data:NSDictionary) -> UserModel {
        var user = UserModel()
        var accountsData:NSArray = data["Accounts"] as NSArray
        // For Swift 1.2, you need this line instead
        // var accountsData:NSArray = data["Accounts"] as! NSArray
        return user
    }
}

5
ApiMapperProtocolの唯一の目的は、一般的な制約に使用されることに注意してください。let x:ApiMapperProtocol = UserMapper()
Ben

18
なぜAppleはすべてをとても直感に反するものにすることを主張するのですか?
deusprogrammer 2016

@Benこの場合、let x:ApiMapperProtocol = UserMapper()をどのように達成しますか?
denis_lor

@denis_lorxがローカルの場合、そのタイプを明示的に指定する必要はないので、let x = UserMapper()
ベンレギエロ

2
ミドルジェネリッククラスで使用している場合ApiMapperProtocol = UserMapper():@BenLeggiero私はちょうどあなたがletのXのようなものを行うことができます見つけたstackoverflow.com/a/54900296/3564632
denis_lor

21

Lou Francoの答えを少し詳しく説明すると、特定のメソッドを使用するメソッドを作成したいApiMapperProtocol場合は、次のようにします。

protocol ApiMapperProtocol {
    associatedtype T
    associatedtype U
    func mapFromSource(T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    // these typealiases aren't required, but I'm including them for clarity
    // Normally, you just allow swift to infer them
    typealias T = NSDictionary 
    typealias U = UserModel

    func mapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel()
        var accountsData: NSArray = data["Accounts"] as NSArray
        // For Swift 1.2, you need this line instead
        // var accountsData: NSArray = data["Accounts"] as! NSArray
        return user
    }
}

class UsesApiMapperProtocol {
    func usesApiMapperProtocol<
        SourceType,
        MappedType,
        ApiMapperProtocolType: ApiMapperProtocol where
          ApiMapperProtocolType.T == SourceType,
          ApiMapperProtocolType.U == MappedType>(
          apiMapperProtocol: ApiMapperProtocolType, 
          source: SourceType) -> MappedType {
        return apiMapperProtocol.mapFromSource(source)
    }
}

UsesApiMapperProtocol指定されたとSourceType互換性のあるsのみを受け入れることが保証されていApiMapperProtocolます:

let dictionary: NSDictionary = ...
let uses = UsesApiMapperProtocol()
let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper()
    source: dictionary)

これは非常に素晴らしい記事であり、賛成です。いくつかのばかげた質問:なぜ彼らはSwift 1.2as!だけasでなく使用することに決めたのですか?2番目:プロトコルに準拠するクラスでtype alias再度定義する必要がある理由(つまりtypealias T = NSDictionary typealias U = UserModel)を教えてください。前もって感謝します。
ウンハイリッヒ2015

なぜからasに切り替えたのかわかりませんas!。devforumを確認してください。
Heath Borders

typealias T=NSDictionarytypealias U=UserModel必須ではありません。それを反映するように例を更新しました。
Heath Borders

2
なので!失敗する可能性があることを示します。開発者にとってより明確になります。
user965972 2015

それは答えの一番下にあります。
Heath Borders 2015年

4

ジェネリックスlet userMapper: ApiMapperProtocol = UserMapper()を持ち、このように宣言するためには、ジェネリック要素を返すプロトコルに準拠したジェネリッククラスが必要です。

protocol ApiMapperProtocol {
    associatedtype I
    associatedType O
    func MapFromSource(data: I) -> O
}

class ApiMapper<I, O>: ApiMapperProtocol {
    func MapFromSource(data: I) -> O {
        fatalError() // Should be always overridden by the class
    }
}

class UserMapper: NSObject, ApiMapper<NSDictionary, UserModel> {
    override func MapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel() as UserModel
        var accountsData:NSArray = data["Accounts"] as NSArray     
        return user
    } 
}

これで、次の方向に特定の実装があるuserMapperとして参照することもできます。ApiMapperUserMapper

let userMapper: ApiMapper = UserMapper()
let userModel: UserModel = userMapper.MapFromSource(data: ...)

この場合、プロトコルを持つことのポイントは何ですか?の宣言では使用されませんuserMapper
alekop

-1

ジェネリックプロトコルを作成して使用する方法

プロトコルジェネリック{

associatedtype T
associatedtype U

func operation(_ t:T)->U

}

//ジェネリックプロトコルを使用

struct Test:Generic {

typealias T = UserModel
typealias U = Any

func operation(_ t: UserModel)->Any {
    let dict = ["name":"saurabh"]
    return dict
    
} 

}


-3

型消去でテンプレートメソッドを使用できます...

protocol HeavyDelegate : class {
  func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}  

class Heavy<P, R> {
    typealias Param = P
    typealias Return = R
    weak var delegate : HeavyDelegate?  
    func inject(p : P) -> R? {  
        if delegate != nil {
            return delegate?.heavy(self, shouldReturn: p)
        }  
        return nil  
    }
    func callMe(r : Return) {
    }
}
class Delegate : HeavyDelegate {
    typealias H = Heavy<(Int, String), String>

    func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
        let h = heavy as! H
        h.callMe("Hello")
        print("Invoked")
        return "Hello" as! R
    }  
}

let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))

2
この投稿には説明が含まれていません。また、stackoverflow.com
questions / 28614990 /
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.