2017-05-28 145 views
3

我在Swift中使用泛型掙扎了一下。無法將類型'child'的返回表達式轉換爲返回類型T

我有以下代碼:

class Parent { 
    init(value: SomeThing) { 
     // ... 
    } 

    func clone<T: Parent>() -> T {   
     return T(value: SomeThing) 
    } 
} 

class Child : Parent { 
    var otherValue: SomeThingElse? 

    override func clone<T>() -> T where T : Parent {   
     let clone: Child = super.clone() 
     clone.otherValue = self.otherValue 
     return clone //ERROR: cannot convert return expression of type 'Child' to return type T 
    } 
} 

的想法是創建一個返回具有相同值的子實例的新副本的簡單方法。 我不想爲每個Child classtype寫出構造函數。 (它在真實課程中有很多參數,我喜歡保持它的清潔)。

我得到的錯誤是: cannot convert return expression of type 'Child' to return type T 建議的解決方案是讓return clone as! T。但這樣我就失去了使用泛型類的理由。

任何想法如何解決這個問題,同時保持它的通用性,而不是寫出每個類中的構造函數?

+0

你應該考慮使用值類型('struct')。值類型在賦值時被複制,因此您不必手動執行。你將失去繼承的能力,但你可以使用協議和擴展來解決這個問題。 – Palle

+0

謝謝,但我不能使用結構,因爲類也需要在Objective-C中使用。 – Peterdk

+0

如果你的類是一個NSObject子類,你可以使用[NSCopying](https://developer.apple.com/reference/foundation/nscopying)。 – Palle

回答

1

您需要具有Self的返回類型,而不是使用約束爲Parent的通用佔位符。使用通用佔位符,您說clone()可以返回的任何實例,該實例繼承自Parent。但事實並非如此 - 您只想返回相同類型的實例作爲接收者,這是Self表示的。

然後,您還需要實施一個required初始化程序,以便它可用於在所有子類上調用,允許在它們上調用clone()而不必覆蓋它。

struct Something {} 
struct SomethingElse {} 

class Parent { 

    var something: Something 

    required init(something: Something) { 
     self.something = something 
    } 

    func clone() -> Self { 
     // call the initialiser on the dynamic metatype of the instance, 
     // ensuring that we're instantiating a Self instance. 
     return type(of: self).init(something: something) 
    } 
} 

Child的實現,那麼應該是簡單的:

class Child : Parent { 

    var somethingElse: SomethingElse? 

    override func clone() -> Self { 
     let clone = super.clone() 
     clone.somethingElse = somethingElse 
     return clone 
    } 
} 

然而不幸的是,呼籲superclone()返回是靜態類型爲Parent,而不是Self一個實例 - 這已經filed as a bug

要解決這一點,你就必須做一些力鑄造兩輪牛車:

override func clone() -> Self { 

    let clone = super.clone() as! Child 
    clone.somethingElse = somethingElse 

    func forceCast<T>(_ value: Child) -> T { return value as! T } 
    return forceCast(clone) 
} 

嵌套forceCast(_:)功能是這裏要解決的是,我們目前還不能直接轉換爲Self的方法(比較Return instancetype in Swift )。在這種情況下施加的力總是會成功,因爲super.clone()將始終返回Self實例,因此在此方法中必須是Child

+0

有趣。如果它不適用於需要forceCast的bug,我會使用它。但目前它看起來更醜陋,只是返回克隆! T'。希望這得到修復!現在我會接受,因爲它看起來像是正確的方式,它可以承受的錯誤。 – Peterdk

+0

@Peterdk做'as的問題!你原來的代碼中的T''可以在實例不能轉換爲'T'時崩潰。考慮一下,如果你有'class Foo:Parent {}',然後有一個叫做'child'的'Child'的實例。做'讓foo:Foo = child.clone()'會崩潰,因爲'Child'的一個實例不能轉換爲'Foo'。使用返回類型'Self'的好處是編譯器知道對於一個'Child'實例,調用'clone()'將返回一個'Child'實例。這只是一個恥辱,它目前不知道這是什麼時候調用它'超':/ – Hamish

+0

好吧,這種情況不會發生在我的情況。我同意使用Self是一個更好的選擇,但不喜歡我需要編碼的額外籃球。 – Peterdk

相關問題