【问题标题】:How to constraint generic type to another generic type in Swift?如何在 Swift 中将泛型类型约束为另一个泛型类型?
【发布时间】:2015-11-29 13:55:36
【问题描述】:

我想做这样的事情:

class Config<T> {
  func configure(x:T)
  // constraint B to be subclass of A
  class func apply<A,B:A>(c:Config<A>, to:B) {
    c.configure(to)
  } 
}

所以稍后,例如,我可以将 Config 应用到 UILabel:

class RedViewConfig<T:UIView> : Config<T> {
  func configure(x:T) {
    x.backgroundColor = .redColor();
  } 
}

let label = UILabel() 
Config.apply(RedViewConfig(), to:label)

或扩展 Config 类:

class RedLabelConfig<T:UILabel> : RedViewConfig<T> {
  func configure(x:T) {
    super.configure(x)
    x.textColor = .redColor();
  } 
}

Config.apply(RedLabelConfig(), to:label)

我尝试这样做,但我无法约束类。所以我尝试使用协议和关联类型,但是在子类化时,我发现覆盖关联类型时出现问题 (like this)。

【问题讨论】:

    标签: swift generics inheritance


    【解决方案1】:

    你真的需要泛型参数B吗?如果你的论点 to: 也被键入为 A,它可以是 A 的任何子类型。像这样:

    class View {}
    class LabelView : View {}
    
    class Config<T> {
      func configure(x:T) { print ("Configured: \(x)") }  
    }
    
    func applyConfig<A> (c:Config<A>, to:A) {
      c.configure(to)
    }
    
    applyConfig(Config<View>(), to: LabelView())
    

    【讨论】:

    • 谢谢!所以在这种情况下,我似乎不需要两种类型。我不知道为什么我认为我需要两个。无论如何,在某些情况下,您需要两种不同的类型。如果我能想到一个例子,我会更新答案。 :)
    【解决方案2】:

    类使这种方式过于复杂。如果可以避免的话,在 Swift 中继承几乎总是一个坏主意。

    结构虽然更接近,但仍然使这有点过于复杂和限制。

    真的,这些配置器只是函数。他们拿走一件东西,然后对它做点什么,什么也不回报。他们只是T -&gt; Void。让我们构建其中的一些。

    func RedViewConfig(view: UIView) { view.backgroundColor = .redColor() }
    func VisibleConfig(view: UIView) { view.hidden = false }
    

    我们可以很容易地使用它们:

    let label = UILabel()
    VisibleConfig(label)
    

    如果它们的类型兼容,我们可以组合它们(如 super,但没有包袱):

    func RedLabelConfig(label: UILabel) {
        RedViewConfig(label)
        label.textColor = .redColor()
    }
    

    我们可以在数据结构中传递它们,编译器将为我们应用正确的协方差:

    let configs = [RedLabelConfig, VisibleConfig]
    // [UILabel -> ()]
    // This has correctly typed visibleConfig as taking `UILabel`,
    // even though visibleConfig takes `UIView`
    
    // And we can apply them
    for config in configs { config(label) }
    

    现在,如果我们想要其他语法,我们也可以很容易地构建它们。更像你原来的东西:

    func applyConfig<T>(f: T -> Void, to: T) {
        f(to)
    }
    applyConfig(VisibleConfig, to: label)
    

    甚至更接近您的原作:

    struct Config {
        static func apply<T>(config: T -> Void, to: T) { config(to) }
    }
    
    Config.apply(VisibleConfig, to: label)
    

    关键是在这里只使用函数就可以让一切变得非常灵活,而不会增加类继承甚至结构的任何复杂性。

    【讨论】:

    • 非常感谢您的回答和示例,Rob。很好的解决方案。我会考虑这个替代方案。我来自 Java,所以我仍然没有“功能心态”。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 2023-03-06
    • 1970-01-01
    相关资源
    最近更新 更多