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,它实现了 CodableMappable 或将来的其他东西,所以编译时通常的类型替换无论如何都必须工作。

有什么建议吗?在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>) 

我认为你做错的是 ResponseMappedClassclassDecodable 另一方面是 协议。而且你不能将 class 和协议类型一起投射。

所以,错误如其所言。

非协议,非class类型'ResponseMappedClass'不能在协议约束类型

中使用

像这样的东西会起作用,因为这两种类型都是协议。

as! (DataResponse<Codable & Decodable>) -> Void