【问题标题】:Can a subclass override a function and make more restrictive return?子类可以覆盖函数并做出更严格的返回吗?
【发布时间】:2017-03-06 00:20:32
【问题描述】:

我正在尝试这种技术:

class Pet {}

class Dog: Pet {}

class House {
    func getPets() -> [Pet] {
        return [Pet]()
    }
}

class DogHouse: House {
    override func getPets() -> [Dog] {
        return [Dog]()
    }
}

DogHouse 类以严格满足HousegetPets 的 API 要求的方式覆盖 House getPets 方法。

但是,Swift 不理解 [Dog][Pet] 并产生错误 Method does not override any method from its superclass

子类有什么方法可以实现比其父类更通用的输入或更严格的输出的 API?

【问题讨论】:

  • 我回答你的问题了吗?
  • 没有真正的理由不能做到这一点——Swift 编译器可以专门处理子类型数组到超类型数组之间的转换。这似乎只是一个边缘情况,因为Array 是泛型的,因此编译器认为它是不变的(泛型是),即使它可以发挥一些魔力使其在大多数地方看起来是协变的(参见例如this Q&A)。另请参阅this related bug report

标签: swift


【解决方案1】:

回答官方提出的问题:是的,Swift 在返回类型中允许更多“受限制”的返回类型。此属性正式称为返回类型协方差。考虑这个例子,它是可编译的 Swift 代码:

class Pet {}

class Dog: Pet {}

class House {
    func getPets() -> Pet {
        return Pet()
    }
}

class DogHouse: House {
    override func getPets() -> Dog {
        return Dog()
    }
}

但是,这里的问题是Array<Dog> 不是比Array<Pet>“更受限制”的类型,相反,Array<Pet> 不是Array<Dog> 的概括。形式上,Array<Dog> 不是Array<Pet> 的协变。

为了说明原因,请考虑以下示例:

class House<T> {
    var occupants = [T]()

    func addOccupant(_ o: T) {
        occupants.append(o)
    }
}

class Pet {}
class Dog: Pet {}
class Cat: Pet {}

class PetHouseBuilder {
    func buildHouse() -> House<Pet> {
        return House()
    }
}

class DogHouseBuilder: PetHouseBuilder {
    // Suppose this were legal
    override func buildHouse() -> House<Dog> {
        return House()
    }
}

// The concrete return type of the object is `House<Dog>`, but
// `PetHouseBuilder.buildHouse` has a static return type of `House<Pet>`,
// so `petHouse` will have an inferred static type of `House<Pet>`
let petHouse = PetHouseBuilder().buildHouse()

let vulnerableLittle? = Cat()
petHouse.addOccupant(vulnerableLittle?)
// Oh boy, now there's a kitten in the dog house ☠️

【讨论】:

  • @Hamish 好点。这个想法是我从最初看到的 Java 转换过来的,但是数组的值语义使其无效。
  • @Hamish 你能想出更好的例子吗?
  • 恐怕不行(我认为 OP 的代码应该编译)——为了说明为什么泛型是不变的,你必须涉及一个泛型引用类型(但是这个逻辑可以应用于任意泛型值类型也是如此,因为它们本身可以包含任意数量的通用引用类型——只是不是专门针对 Array,因为它具有写入值语义的复制)。
  • @Hamish 我想我有一个工作示例,它依赖于通用引用类型
  • 是的,这行得通(或者我应该说“行不通”:P)。虽然它不能证明 OP 的代码不应该编译,只是不应该允许任意泛型类型是协变的(Array 可以安全地成为这个规则的例外,因为它的值语义)。
【解决方案2】:

@亚历山大

您好,您可能知道,为什么您的示例不适用于协议?

protocol IPet {}

protocol IDog: IPet {}

class Pet: IPet {}

class Dog: Pet, IDog {}

class House {
    func getPets() -> IPet {
        return Pet()
    }
}

class DogHouse: House {
    override func getPets() -> IDog //Method does not override any method from its superclass
    { 
        return Dog()
    }
}

谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-18
    • 2023-03-28
    • 1970-01-01
    • 2021-05-21
    相关资源
    最近更新 更多