Swift 4: 泛型转换问题
Swift 4: cast issue with Generics
作为 Objective-C 开发人员多年后,我是 Swift 的新手。我正在努力理解类型转换如何与泛型一起工作。
我有两个函数正在使用 Alamofire + Codable 和 Alamofire + ObjectMapper 进行对象映射。像这样:
public func performRestOperationWithDecodable<ResponseClass: Decodable>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters,
encoding: ParameterEncoding,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: @escaping (_ responseObject: DataResponse<ResponseClass>) -> Void,
errorBlock:@escaping (_ error: Error, _ validResponse: Bool) -> Void) {
let restConfiguration = self.setupRestOperation(
savedAuthType: savedAuthType,
pathUrl: pathUrl,
activityIndicator: activityIndicator)
Alamofire
.request(
restConfiguration.url,
method: .get,
parameters: parameters,
encoding: encoding,
headers: restConfiguration.headers)
.validate(
contentType: Constants.Network.DefaultValidContentTypeArray)
.responseDecodableObject(
queue: self.dispatchQueue,
keyPath: nil,
decoder: JSONDecoder(),
completionHandler: { (responseObject: DataResponse<ResponseClass>) in
self.completionRestOperation(
responseObject: responseObject,
activityIndicator: activityIndicator,
successBlock: successBlock,
errorBlock: errorBlock)
})
}
public func performRestOperationWithObjectMapper<ResponseClass: BaseMappable>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters,
encoding: ParameterEncoding,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: @escaping (_ responseObject: DataResponse<ResponseClass>) -> Void,
errorBlock: @escaping (_ error: Error, _ validResponse: Bool) -> Void) {
let restConfiguration = self.setupRestOperation(
savedAuthType: savedAuthType,
pathUrl: pathUrl,
activityIndicator: activityIndicator)
Alamofire
.request(
restConfiguration.url,
method: .get,
parameters: parameters,
encoding: encoding,
headers: restConfiguration.headers)
.validate(
contentType: Constants.Network.DefaultValidContentTypeArray)
.responseObject(
queue: self.dispatchQueue,
keyPath: nil,
mapToObject: nil,
context: nil,
completionHandler: { (responseObject: DataResponse<ResponseClass>) in
self.completionRestOperation(
responseObject: responseObject,
activityIndicator: activityIndicator,
successBlock: successBlock,
errorBlock: errorBlock)
})
}
每个函数都有一个通用类型,符合所用映射器所需的适当协议(可解码用于 Codable,可映射用于 ObjectMapper)。这些功能按预期编译和工作。
现在我正在尝试编写具有第三个泛型类型的第三个函数,但不符合任何协议,并根据配置参数强制转换为适当的泛型。类似的东西:
public func performRestOperation<ResponseMappedClass :AnyObject>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters = [:],
encoding: ParameterEncoding = URLEncoding.queryString,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: @escaping (_ responseObject: ResponseMappedClass) -> Void,
errorBlock: @escaping (_ error: Error, _ validResponse: Bool) -> Void) {
if self.canDoRestOperation(errorBlock: errorBlock) != true {
return
}
switch self.mappingType {
case .Codable:
self.performRestOperationWithDecodable(
restOperationType,
pathUrl: pathUrl,
parameters: parameters,
encoding: encoding,
savedAuthType: savedAuthType,
activityIndicator: activityIndicator,
successBlock: { (responseObject: DataResponse<ResponseMappedClass>) in
let response: ResponseMappedClass = responseObject.result.value!
successBlock(response)
} as! (DataResponse<ResponseMappedClass & Decodable>) -> Void,
errorBlock: { (error: Error, validResponse: Bool) in
errorBlock(error, validResponse)
})
case .ObjectMapper:
// TODO
break
}
}
根据指令:as! (DataResponse<ResponseMappedClass & Decodable>) -> Void
,我正在尝试从 "generic" class 类型 ResponseMappedClass
转换为相同的 class,但是Codable
支持。
但是该指令无法编译:
Non-protocol, non-class type 'ResponseMappedClass' cannot be used
within a protocol-constrained type
在所有这些过程之后,各种泛型将表示相同的 class,例如 SomeModelObject
,它实现了 Codable
、Mappable
或将来的其他东西,所以编译时通常的类型替换无论如何都必须工作。
有什么建议吗?在Swift是完全不可能的吗?
我认为不可能将通用类型与协议结合使用。编译器不会将它们视为 class 或协议类型。您的代码还有一件事是错误的,您正在使用 self.mappingType
来决定它是 Decodable
还是 Mapable
类型。如果 self.mappingType == .Codable
,但 ResponseMappedClass
不确认 Decodable
协议怎么办?我可以向您建议的是函数重载。创建 3 个函数:
public func performRestOperation<ResponseMappedClass: AnyObject>(...) where ResponseMappedClass: Decodable {
}
public func performRestOperation<ResponseMappedClass: AnyObject>(...) where ResponseMappedClass: BaseMappable {
}
public func performRestOperation<ResponseMappedClass: AnyObject>(...) {
}
在最后一个只是抛出一个错误。将根据泛型参数类型调用正确的函数。
(DataResponse<ResponseMappedClass & Decodable>)
我认为你做错的是 ResponseMappedClass
是 class 而 Decodable
另一方面是 协议。而且你不能将 class 和协议类型一起投射。
所以,错误如其所言。
非协议,非class类型'ResponseMappedClass'不能在协议约束类型
中使用
像这样的东西会起作用,因为这两种类型都是协议。
as! (DataResponse<Codable & Decodable>) -> Void
作为 Objective-C 开发人员多年后,我是 Swift 的新手。我正在努力理解类型转换如何与泛型一起工作。
我有两个函数正在使用 Alamofire + Codable 和 Alamofire + ObjectMapper 进行对象映射。像这样:
public func performRestOperationWithDecodable<ResponseClass: Decodable>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters,
encoding: ParameterEncoding,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: @escaping (_ responseObject: DataResponse<ResponseClass>) -> Void,
errorBlock:@escaping (_ error: Error, _ validResponse: Bool) -> Void) {
let restConfiguration = self.setupRestOperation(
savedAuthType: savedAuthType,
pathUrl: pathUrl,
activityIndicator: activityIndicator)
Alamofire
.request(
restConfiguration.url,
method: .get,
parameters: parameters,
encoding: encoding,
headers: restConfiguration.headers)
.validate(
contentType: Constants.Network.DefaultValidContentTypeArray)
.responseDecodableObject(
queue: self.dispatchQueue,
keyPath: nil,
decoder: JSONDecoder(),
completionHandler: { (responseObject: DataResponse<ResponseClass>) in
self.completionRestOperation(
responseObject: responseObject,
activityIndicator: activityIndicator,
successBlock: successBlock,
errorBlock: errorBlock)
})
}
public func performRestOperationWithObjectMapper<ResponseClass: BaseMappable>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters,
encoding: ParameterEncoding,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: @escaping (_ responseObject: DataResponse<ResponseClass>) -> Void,
errorBlock: @escaping (_ error: Error, _ validResponse: Bool) -> Void) {
let restConfiguration = self.setupRestOperation(
savedAuthType: savedAuthType,
pathUrl: pathUrl,
activityIndicator: activityIndicator)
Alamofire
.request(
restConfiguration.url,
method: .get,
parameters: parameters,
encoding: encoding,
headers: restConfiguration.headers)
.validate(
contentType: Constants.Network.DefaultValidContentTypeArray)
.responseObject(
queue: self.dispatchQueue,
keyPath: nil,
mapToObject: nil,
context: nil,
completionHandler: { (responseObject: DataResponse<ResponseClass>) in
self.completionRestOperation(
responseObject: responseObject,
activityIndicator: activityIndicator,
successBlock: successBlock,
errorBlock: errorBlock)
})
}
每个函数都有一个通用类型,符合所用映射器所需的适当协议(可解码用于 Codable,可映射用于 ObjectMapper)。这些功能按预期编译和工作。 现在我正在尝试编写具有第三个泛型类型的第三个函数,但不符合任何协议,并根据配置参数强制转换为适当的泛型。类似的东西:
public func performRestOperation<ResponseMappedClass :AnyObject>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters = [:],
encoding: ParameterEncoding = URLEncoding.queryString,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: @escaping (_ responseObject: ResponseMappedClass) -> Void,
errorBlock: @escaping (_ error: Error, _ validResponse: Bool) -> Void) {
if self.canDoRestOperation(errorBlock: errorBlock) != true {
return
}
switch self.mappingType {
case .Codable:
self.performRestOperationWithDecodable(
restOperationType,
pathUrl: pathUrl,
parameters: parameters,
encoding: encoding,
savedAuthType: savedAuthType,
activityIndicator: activityIndicator,
successBlock: { (responseObject: DataResponse<ResponseMappedClass>) in
let response: ResponseMappedClass = responseObject.result.value!
successBlock(response)
} as! (DataResponse<ResponseMappedClass & Decodable>) -> Void,
errorBlock: { (error: Error, validResponse: Bool) in
errorBlock(error, validResponse)
})
case .ObjectMapper:
// TODO
break
}
}
根据指令:as! (DataResponse<ResponseMappedClass & Decodable>) -> Void
,我正在尝试从 "generic" class 类型 ResponseMappedClass
转换为相同的 class,但是Codable
支持。
但是该指令无法编译:
Non-protocol, non-class type 'ResponseMappedClass' cannot be used within a protocol-constrained type
在所有这些过程之后,各种泛型将表示相同的 class,例如 SomeModelObject
,它实现了 Codable
、Mappable
或将来的其他东西,所以编译时通常的类型替换无论如何都必须工作。
有什么建议吗?在Swift是完全不可能的吗?
我认为不可能将通用类型与协议结合使用。编译器不会将它们视为 class 或协议类型。您的代码还有一件事是错误的,您正在使用 self.mappingType
来决定它是 Decodable
还是 Mapable
类型。如果 self.mappingType == .Codable
,但 ResponseMappedClass
不确认 Decodable
协议怎么办?我可以向您建议的是函数重载。创建 3 个函数:
public func performRestOperation<ResponseMappedClass: AnyObject>(...) where ResponseMappedClass: Decodable {
}
public func performRestOperation<ResponseMappedClass: AnyObject>(...) where ResponseMappedClass: BaseMappable {
}
public func performRestOperation<ResponseMappedClass: AnyObject>(...) {
}
在最后一个只是抛出一个错误。将根据泛型参数类型调用正确的函数。
(DataResponse<ResponseMappedClass & Decodable>)
我认为你做错的是 ResponseMappedClass
是 class 而 Decodable
另一方面是 协议。而且你不能将 class 和协议类型一起投射。
所以,错误如其所言。
非协议,非class类型'ResponseMappedClass'不能在协议约束类型
中使用像这样的东西会起作用,因为这两种类型都是协议。
as! (DataResponse<Codable & Decodable>) -> Void