带泛型的Protocol作为属性调用方法时Swift编译报错
问题描述
我定义了带有关联类型的协议SomeObjectFactory和ConfigurationFactory,在Builder类中使用any ConfigurationFactory类型的属性调用configWithExperience方法时,Swift 5.7编译报错:
Member 'configWithExperience' cannot be used on value of type 'any ConfigurationFactory'; consider using a generic constraint instead
我的需求是通过Builder的configurationFactory实例创建SomeObjectConfiguration对象,相关实现代码如下:
import Combine import Foundation final class SomeObject<T: Combine.Scheduler> {} struct Experience { let id: String } struct SomeObjectConfiguration<T: Combine.Scheduler> { let scheduler: T } protocol SomeObjectFactory { associatedtype T: Combine.Scheduler func createSomeObjectWithConfiguration(_ config: SomeObjectConfiguration<T>) -> SomeObject<T> } protocol ConfigurationFactory { associatedtype T: Combine.Scheduler func configWithExperience(_ experience: Experience) -> SomeObjectConfiguration<T> } final class Builder<T: Combine.Scheduler> { private let configurationFactory: any ConfigurationFactory init(configurationFactory: any ConfigurationFactory) { self.configurationFactory = configurationFactory } func createSomeObject(_ experience: Experience) { let someObjectConfiguration: SomeObjectConfiguration<T> = configurationFactory.configWithExperience(experience) } }
问题原因
any ConfigurationFactory是存在类型(existential type),它仅保证实例实现了ConfigurationFactory协议,但无法确定其关联类型T与Builder类的泛型T为同一类型。编译器无法确认configWithExperience返回的SomeObjectConfiguration<X>能安全转换为SomeObjectConfiguration<T>,因此抛出错误。
解决方案
方案一:通过泛型约束绑定类型
给Builder添加额外泛型参数,让ConfigurationFactory的关联类型与Builder的泛型T强制匹配:
import Combine import Foundation final class SomeObject<T: Combine.Scheduler> {} struct Experience { let id: String } struct SomeObjectConfiguration<T: Combine.Scheduler> { let scheduler: T } protocol SomeObjectFactory { associatedtype T: Combine.Scheduler func createSomeObjectWithConfiguration(_ config: SomeObjectConfiguration<T>) -> SomeObject<T> } protocol ConfigurationFactory { associatedtype T: Combine.Scheduler func configWithExperience(_ experience: Experience) -> SomeObjectConfiguration<T> } final class Builder<T: Combine.Scheduler, C: ConfigurationFactory> where C.T == T { private let configurationFactory: C init(configurationFactory: C) { self.configurationFactory = configurationFactory } func createSomeObject(_ experience: Experience) { let someObjectConfiguration = configurationFactory.configWithExperience(experience) // 后续可基于配置创建SomeObject实例 // let factory: SomeObjectFactory = ... // let someObject = factory.createSomeObjectWithConfiguration(someObjectConfiguration) } }
方案二:使用泛型协议存在类型(Swift 5.7+)
利用Swift 5.7支持的泛型协议存在类型,直接限定ConfigurationFactory的关联类型为Builder的泛型T:
import Combine import Foundation final class SomeObject<T: Combine.Scheduler> {} struct Experience { let id: String } struct SomeObjectConfiguration<T: Combine.Scheduler> { let scheduler: T } protocol SomeObjectFactory { associatedtype T: Combine.Scheduler func createSomeObjectWithConfiguration(_ config: SomeObjectConfiguration<T>) -> SomeObject<T> } // 显式声明协议的关联类型为泛型参数 protocol ConfigurationFactory<T> { associatedtype T: Combine.Scheduler func configWithExperience(_ experience: Experience) -> SomeObjectConfiguration<T> } final class Builder<T: Combine.Scheduler> { private let configurationFactory: any ConfigurationFactory<T> init(configurationFactory: any ConfigurationFactory<T>) { self.configurationFactory = configurationFactory } func createSomeObject(_ experience: Experience) { let someObjectConfiguration = configurationFactory.configWithExperience(experience) } }
说明
两种方案都能解决类型不匹配问题:方案一通过泛型约束让编译器明确类型关联关系;方案二直接限定存在类型的关联类型,简化代码结构,更贴合Swift 5.7+的语法特性。
内容的提问来源于stack exchange,提问作者DesperateLearner

