2015-11-29 208 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,所以我仍然沒有「功能性思維」。 :) –