Swift泛型优化:如何从已用泛型类型推断通用类型?
我完全理解你的痛点——每次创建RemoteFileDetectionPerformer时都要重复写已经被SamplePerformer包含的Int、String类型,不仅繁琐还容易出错。其实我们可以通过重构泛型结构,让Swift自动帮我们推断这些重复的类型参数,不用手动重复指定。
核心思路
问题出在RemoteFileDetectionPerformer当前的泛型定义上:它把ResultType、LocalModelType和LocalFileDetectionPerformerType并列作为泛型参数,但后一个参数其实已经蕴含了前两个的类型信息。我们可以通过协议关联类型或者类型别名提取的方式,让Swift从LocalFileDetectionPerformerType自动推断出前两个类型。
方案一:用协议抽象关联类型(推荐,更符合Swift类型系统设计)
1. 定义抽象协议
首先创建两个协议,把泛型类型抽象为关联类型,这样我们可以从具体的执行器类型中提取所需的类型信息:
// 定义检测执行器的基础协议,关联结果类型 protocol DetectionPerformerProtocol { associatedtype ResultType } // 定义支持本地模型加载的执行器协议,关联本地模型类型,并要求初始化方法 protocol DownloadableDetectionPerformerProtocol: DetectionPerformerProtocol { associatedtype LocalModelType required init(localModelURL: URL) }
2. 修改现有基类遵守协议
让你的现有基类实现这些协议,确保类型关联关系被正确识别:
// MARK: - 基类修改 class DetectionPerformer<ResultType>: DetectionPerformerProtocol {} class LocalFileDetectionPerformer<ResultType, LocalModelType>: DetectionPerformer<ResultType>, DownloadableDetectionPerformerProtocol { required init(localModelURL: URL) {} } class DownloadableDetectionPerformer<ResultType, LocalModelType>: LocalFileDetectionPerformer<ResultType, LocalModelType> {}
3. 重构RemoteFileDetectionPerformer
现在修改RemoteFileDetectionPerformer的泛型定义,只保留LocalFileDetectionPerformerType作为泛型参数,通过协议的关联类型自动获取ResultType和LocalModelType:
class RemoteFileDetectionPerformer<LocalPerformer: DownloadableDetectionPerformerProtocol>: DetectionPerformer<LocalPerformer.ResultType> { // 通过关联类型提取所需类型,无需手动指定 typealias ResultType = LocalPerformer.ResultType typealias LocalModelType = LocalPerformer.LocalModelType private let localFileDetectionPerformer: LocalPerformer init(remoteModelURL: URL) { let localModelURL = Self.localModelURL(for: remoteModelURL) localFileDetectionPerformer = LocalPerformer(localModelURL: localModelURL) } static func localModelURL(for url: URL) -> URL { url.appendingPathExtension("local") } }
4. 优化后的使用体验
现在创建detectorB时,只需要指定SamplePerformer即可,Swift会自动从SamplePerformer中推断出ResultType=Int和LocalModelType=String:
// 实现具体的执行器类 class SamplePerformer: DownloadableDetectionPerformer<Int, String> {} // 现在创建detectorB时无需重复指定泛型类型 let detectorB = Detector(performer: RemoteFileDetectionPerformer<SamplePerformer>(remoteModelURL: URL(string: "")!))
方案二:直接用类型别名提取(无需新增协议)
如果你不想引入新的协议,也可以直接通过类型别名从LocalFileDetectionPerformerType中提取泛型类型:
1. 给DownloadableDetectionPerformer添加类型别名
先给DownloadableDetectionPerformer添加两个类型别名,方便后续提取:
class DownloadableDetectionPerformer<ResultType, LocalModelType>: LocalFileDetectionPerformer<ResultType, LocalModelType> { typealias PerformerResultType = ResultType typealias PerformerLocalModelType = LocalModelType }
2. 重构RemoteFileDetectionPerformer
修改泛型定义,利用通配符_让Swift自动推断前两个类型,然后通过类型别名提取:
class RemoteFileDetectionPerformer<LocalPerformer: DownloadableDetectionPerformer<_, _>>: DetectionPerformer<LocalPerformer.PerformerResultType> { typealias ResultType = LocalPerformer.PerformerResultType typealias LocalModelType = LocalPerformer.PerformerLocalModelType private let localFileDetectionPerformer: LocalPerformer init(remoteModelURL: URL) { let localModelURL = Self.localModelURL(for: remoteModelURL) localFileDetectionPerformer = LocalPerformer(localModelURL: localModelURL) } static func localModelURL(for url: URL) -> URL { url.appendingPathExtension("local") } }
3. 使用方式和方案一一致
同样只需要指定SamplePerformer,Swift会自动推断出对应的类型:
let detectorB = Detector(performer: RemoteFileDetectionPerformer<SamplePerformer>(remoteModelURL: URL(string: "")!))
为什么这样可行?
两种方案的核心都是让Swift从已有的泛型参数中自动推导依赖类型,避免了手动重复指定。方案一通过协议关联类型让类型关系更清晰、扩展性更强;方案二更简洁,适合不想引入额外协议的场景。
内容的提问来源于stack exchange,提问作者Damian Dudycz

