无法将类型'child'的返回表达式转换为返回类型T

问题描述:

我在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

您需要具有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