【问题标题】:Implementing generic interfaces in Apple's Swift在 Apple 的 Swift 中实现通用接口
【发布时间】:2015-04-25 15:33:48
【问题描述】:

我有一个带有属性的类,该属性应该具有在其他语言中称为通用(或模板)接口的类型。当我试图在 Swift 中模仿这种行为时,我无法让协议与这个想法一起工作。例如:

protocol P {
    typealias T
    func returnT() -> T
}

class G<T>: P {
    var t: T

    init(t: T) {
        self.t = t
    }

    func returnT() -> T {
        return t
    }
}

class C {
    var p: P<Int>               // Cannot specialize non-generic type 'P'

    init(instanceOfP: P<Int>) { // Cannot specialize non-generic type 'P'
        p = instanceOfP
    }

    func usePsT() -> Int {
        return p.returnT()      // 'P' does not have a member named  'returnT'
    }
}

来自编译器的错误报告为 cmets。在我看来,这种情况应该没有问题:但由于 Swift 的协议不能是泛型(他们使用这种晦涩的 typealias 语法代替)C 无法知道实现 P 的每个类都可以专门用于Int。是否有一种 Swift-y 方式来正确表示这种情况?或者是否有任何已知的解决方法,这样我就不必强制或去概括类结构?

【问题讨论】:

  • 不幸的是,至少目前 Swift 的协议不支持泛型类型参数。只允许关联类型,以使它们通用。我一直认为这很不方便。

标签: swift templates generics interface protocols


【解决方案1】:

您的协议中并不真正需要泛型(使用Any) - 您的class G&lt;T&gt; 需要它。你可以这样做...

protocol P {
    func returnT() -> Any
}

class G<T>: P {
    var t: T

    init(t: T) {
        self.t = t
    }

    func returnT() -> Any {
        return t
    }
}

class C {
    var p: P

    init(instanceOfP: P) {
        p = instanceOfP
    }

    func usePsT() -> Any {
        return p.returnT()
    }
}

let gi = G<Int>(t: 7) // {t 7}
let ci = C(instanceOfP: gi) // {{t 7}}
ci.usePsT() // 7

let gs = G<String>(t: "Hello")
let cs = C(instanceOfP: gs)
cs.usePsT() // "Hello"

【讨论】:

  • 我实际上可能需要将 P 和 G 暴露给第三方,所以我想保留整个类型安全的东西......
  • 我想我缺乏想象力 - 为什么需要协议?为什么不将G 定义为具有func returnT() -&gt; T?仅当您需要多个独立的“类 G”类时才需要该协议(您似乎不需要,因为 G 通用的)...
  • 它实际上是关于 2D Map 实现,它可以或不能保留有关其键的信息,以换取其性能。我想将这两种可能性实现为单个 Map 接口的两个不同实现(请原谅冗余),以便第三方可以声明其类(C 所扮演的角色)以使 Map 变量引用特定实现,而无需使用代码必须知道可能是什么特定的实现,因为它们共享共同的地图功能。
  • 我为我自己的这种类型的特定使用案例找到了一种解决方法,但我正在用 RAM 使用来换取 CPU 时间,虽然现在这对我有好处,但我不知道它是否会晚一点,或者是否适合其他人。在这种情况下可能还有其他类结构,所以无论如何找到一个一致的解决方案会很好。
【解决方案2】:

我的解决方案并不理想,但为我省去了很多麻烦,代码仍然可读... 我使用带有开放空函数的基类而不是接口。 像这样:

public class CSTableControllerFilter<Row: CSTableControllerRow, Data> {
    open func filter(data: [Row]) -> [Row] { data }

    open func onReloadDone(in controller: CSTableController<Row, Data>) {}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    • 1970-01-01
    • 2012-01-08
    相关资源
    最近更新 更多