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

时间:2017-05-28 23:26:27

标签: swift generics

我在Swift中对泛型有点挣扎。

我有以下代码:

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。但是这样我就失去了使用泛型类的理由。

任何想法如何解决这个问题,同时保持它的通用性,而不是在每个类中写出构造函数?

1 个答案:

答案 0 :(得分:3)

您需要返回类型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
    }
}

但遗憾的是,在clone()上调用super会返回一个静态输入为Parent而不是Self的实例 - 这已经是filed as a bug

要解决这个问题,你必须做一些强制投射hackery:

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