2015-11-29 33 views
1

我想要做这样的事情:如何在Swift中将泛型类型约束为另一个泛型类型?

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) 
    } 
} 

所以后来,例如,我可以申请配置到一个UILabel:

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

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

或扩展配置类:

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

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

我试图做到这一点,但我无法约束类。所以我尝试了协议和相关类型,但是当我重写相关类型时,我发现问题(like this)。

回答

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()) 
+0

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

1

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

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

真的,这些配置器只是功能。他们拿一件东西,他们做了一些事情,什么也没有返回。他们只是T -> 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) 

的一点是,仅仅使用功能使得这里的一切非常灵活,不添加任何类的继承,甚至复杂的结构。

+0

非常感谢你的答案和例子,罗布。好的解决方案我会考虑这个选择。我来自Java,所以我仍然没有“功能性思维”。 :) –