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

5

我在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类型的构造函数编写代码。(在真正的类中有很多参数,我想保持它的干净整洁)。
我得到的错误是:cannot convert return expression of type 'Child' to return type T。 建议的解决方案是使用return clone as! T。 但这样我就失去了使用泛型类的理由。
你有什么好主意可以在保持通用性的同时解决这个问题,而不必在每个类中都写出构造函数呢?

你应该考虑使用值类型(struct)。值类型在赋值时被复制,所以你不必手动进行。你会失去子类的能力,但是你可以使用协议和扩展来解决这个问题。 - Palle
谢谢,但我不能使用结构体,因为这个类还需要在Objective-C中使用。 - Peterdk
如果你的类是NSObject子类,你可以使用NSCopying - Palle
1个回答

4
你需要使用返回类型为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)
    }
}
< p >实现Child应该尽可能简单:< /p >
class Child : Parent {

    var somethingElse: SomethingElse?

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

然而不幸的是,在super上调用clone()会返回一个静态类型为Parent而不是Self的实例——这已被记录为错误
为了解决这个问题,你需要进行一些强制转换的 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的问题(请参见Swift中返回instancetype)。在这种情况下,这两个强制转换将始终成功,因为super.clone()将始终返回一个Self实例,因此在此方法中必须是Child

有趣。如果不是因为需要强制转换的 bug,我会使用这个方法。但目前它看起来比只返回 clone as! T 更丑陋。希望这个问题能得到解决!现在我会接受,因为它看起来是正确的做法,尽管存在 bug。 - Peterdk
@Peterdk 在你的原始代码中使用 as! T 的问题在于,当实例无法转换为 T 时,它可能会崩溃。考虑一下如果你有一个名为 Child 的实例,并且有一个 class Foo : Parent {},那么执行 let foo: Foo = child.clone() 将会崩溃,因为 Child 实例无法转换为 Foo。使用返回类型 Self 的好处是编译器知道对于 Child 实例,在其上调用 clone() 将返回一个 Child 实例。只可惜目前在调用 super 时编译器还不知道这一点 :/ - Hamish
好的,那种情况在我的情况下不会发生。我同意使用Self是更好的选择,但不喜欢需要编写额外代码的麻烦。 - Peterdk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接