Swift:Covariant覆盖?

时间:2016-09-05 14:21:06

标签: swift covariance

有没有办法在Swift中支持协变返回类型?

例如,我想支持以下情况:

class Animal {}
class Dog : Animal{}
class Cat : Animal {}

class AnimalResidency{
    func getAllAnimals() -> Array<Animal> {
        return []
    }
}
class Cattery : AnimalResidency{
    override func getAllAnimals() -> Array<Cat> {
        return [Cat(), Cat()]
    }
}
class DogKennel : AnimalResidency {
    override func getAllAnimals() -> Array<Dog> {
        return [Dog(), Dog(), Dog(), Dog()]
    }
}

重写的func会产生编译器错误,因为覆盖签名与基本定义不完全匹配,即使它清楚地看到覆盖返回的内容仍然符合基本定义的契约

我有什么方法可以实现上述目标吗?我甚至欣赏Swift 3的答案。

4 个答案:

答案 0 :(得分:4)

我不确定是否有必要完全你要求的内容 - 即覆盖 getAllAnimals而不是重载它。使用泛型是一种可能的解决方案 - 看看这是否适合您:

class Animal { var description: String { return "Animal" } }
class Dog : Animal { override var description: String { return "Dog" } }        
class Cat : Animal { override var description: String { return "Cat" } }

class AnimalResidency<T: Animal>{
    func getAllAnimals<T>() -> Array<T> {
        return []
    }
}

class Cattery : AnimalResidency<Cat> {
    func getAllAnimals() -> Array<Cat> {
        return [Cat()]
    }
}

class DogKennel : AnimalResidency<Dog> {
    func getAllAnimals() -> Array<Dog> {
        return [Dog(), Dog()]
    }
}

let c = Cattery()
c.getAllAnimals().first?.description // "Cat"
let d = DogKennel()
d.getAllAnimals().first?.description // "Dog"

然而,我自己的想法不会使用两个并行的类层次结构,而是尝试更像这样的东西......

class Animal {
    var description: String { return "Animal" }
    required init() {}
}
class Dog : Animal {
    override var description: String { return "Dog" }
}
class Cat : Animal {
    override var description: String { return "Cat" }
}

extension Animal {
    class func home() -> [Animal] {
        return [self.init()]
    }
}

let c = Cat.home().first?.description // "Cat"
let d = Dog.home().first?.description // "Dog"

答案 1 :(得分:1)

Swift使用面向协议的范例。所以基于你的要求。最好在你的情况下使用协议和PAT。

What are PATs.

protocol BaseProtocol {
    //PAT's
    typealias ReturnType: BaseType
    func someFunction() -> ReturnType
}

class SomeClass : BaseProtocol {
    func someFunction() -> BaseType {  }
}

答案 2 :(得分:0)

正如您已经注意到的,签名必须完全相同。

但您可以在[Base] - 类中使用Derived作为返回类型。

class Base {
    var a = "A" //just a property to check if you can access properties of this class

    var test: [Base] {
        return [self]
    }

    func test2() -> [Base] {
        return [self]
    }
}

class Derived: Base {
    var b = "B" //just a property to check if you can access properties of this class

    override var test: [Base] {
        return [self]
    }

    override func test2() -> [Base] {
        return [self]
    }
}

您可以使用test2()test(计算属性)。

在您可以访问存储在数组中的对象的属性之前,您必须将其强制转换为 Derived

let d = Derived()
let dArray = d.test2()

print((dArray[0] as! Derived).b) //Prints "B"

答案 3 :(得分:0)

正如FelixSFD的回答中所提到的,你的目标与面向接口的范式相冲突。 HeadOffice应使用Office的界面,例如:

let staff: [Office] = [Office(), HeadOffice()]
for person in staff {
    // every `person` responds to `Office` interface
}

在你的情况下,我建议使用泛型,因为它们是为这类东西而设计的:

func getAllStaff<T>(obj: T) -> Array<T?> {
    return [obj]
}

您还可以根据自己的需要实施getAllStaff作为课程方法......