You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何定义含不同类型泛型属性实例的数组?

Swift中泛型类型的类型擦除方案:隐藏泛型参数E

嗨,这个问题正好踩中了Swift泛型和Java通配符的核心差异——Swift确实没有像Java?那样的泛型通配符,但我们可以通过类型擦除的方式来实现你想要的效果:把不同E类型的Property<Test, E>统一放进同一个数组,同时完全隐藏E这个泛型参数,而且这个方案不依赖Hashable,可以通用到任何场景。

核心思路

我们需要把Property<T, E>中与E无关的公共操作抽象成一个协议,然后用一个“包装器”结构体来捕获具体Property的实现细节,对外只暴露T类型,从而擦除掉E的类型信息。

具体实现步骤

1. 定义抽象协议,剥离E类型

首先创建一个只关注核心操作的协议,不需要泛型参数E,只关联T:

public protocol AnyProperty<T> {
    associatedtype T
    // 你的核心操作:获取哈希值
    func getMemberHashValue(instance: T) -> Int
    // 你的核心操作:比较属性值
    func equalsMembers(instance1: T, instance2: T) -> Bool
}

注:这里用了Swift 5.7+支持的关联类型协议泛型声明,让T的关联更清晰,低版本可以把associatedtype T放在协议内部。

2. 让原Property结构体遵守协议

修改你的Property结构体,让它遵守上面的协议(其实你的原有方法已经完全符合协议要求,只需要加个协议声明就行):

public struct Property<T, E: Hashable>: AnyProperty {
    let getter: (T) -> E
    
    func getMemberHashValue(instance: T) -> Int {
        return getter(instance).hashValue
    }
    
    func equalsMembers(instance1: T, instance2: T) -> Bool {
        return getter(instance1) == getter(instance2)
    }
}

3. 实现类型擦除的包装器

这个包装器是关键,它会把具体Property<T, E>的方法捕获到闭包里,对外只暴露T类型:

public struct AnyPropertyWrapper<T>: AnyProperty {
    // 用闭包捕获具体Property的实现
    private let _getMemberHashValue: (T) -> Int
    private let _equalsMembers: (T, T) -> Bool
    
    // 初始化方法接受任意符合AnyProperty且关联类型为T的实例
    init<U: AnyProperty>(_ property: U) where U.T == T {
        _getMemberHashValue = property.getMemberHashValue
        _equalsMembers = property.equalsMembers
    }
    
    // 实现协议方法,直接调用捕获的闭包
    func getMemberHashValue(instance: T) -> Int {
        return _getMemberHashValue(instance)
    }
    
    func equalsMembers(instance1: T, instance2: T) -> Bool {
        return _equalsMembers(instance1, instance2)
    }
}

4. 调整Test结构体的properties类型

现在你就可以把properties的类型指定为[AnyPropertyWrapper<Test>],不管原来的E是Int还是Double,都能被统一包装进去:

struct Test {
    let a: Int
    let b: Double
    
    let properties: [AnyPropertyWrapper<Test>] = [
        AnyPropertyWrapper(Property(getter: { $0.a })),
        AnyPropertyWrapper(Property(getter: { $0.b }))
    ]
}

方案的通用性说明

这个方案完全不依赖Hashable约束——如果你的场景不需要哈希操作,只需要比较,你可以:

  • 去掉Property中E: Hashable的约束
  • 修改AnyProperty协议,只保留equalsMembers方法
  • 调整AnyPropertyWrapper的闭包和实现

这种方式本质是把“具体类型的操作”转换成“闭包捕获的行为”,只要你能把需要的操作抽象到协议里,就能擦除掉不需要的泛型参数,完美替代Java通配符的场景。

对比AnyHashable的方案:用AnyHashable是把E转换成统一的类型,但会丢失类型信息,且只能用于Hashable场景;而类型擦除的方案保留了操作的语义,更灵活通用。

内容的提问来源于stack exchange,提问作者Feuermurmel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:25:55