如何在 Swift 中创建通用协议?

我想用一个方法创建一个协议,该方法接受一个通用输入并返回一个通用值。

这就是我到目前为止所尝试的,但是它产生了语法错误。

使用未声明的标识符 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
}
}
63794 次浏览

It's a little different for protocols. Look at "Associated Types" in Apple's documentation.

This is how you use it in your example

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
}
}

To expound on Lou Franco's answer a bit, If you wanted to create a method that used a particular ApiMapperProtocol, you do so thusly:

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 is now guaranteed to only accept SourceTypes compatible with the given ApiMapperProtocol:

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

You can use templates methods with type-erasure...

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"))

In order to achieve having generics and as well having it declare like this let userMapper: ApiMapperProtocol = UserMapper() you have to have a Generic Class conforming to the protocol which returns a generic element.

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
}
}

Now you can also refer to userMapper as an ApiMapper which have a specific implementation towards UserMapper:

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

How to create and use generic Protocol:

protocol Generic {
    

associatedtype T
associatedtype U


func operation(_ t: T) -> U
}




// Use Generic Protocol


struct Test: Generic {


typealias T = UserModel
typealias U = Any
    

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