swift - Swift 中运算符重载的多态性

标签 swift

我遇到了一个问题,我希望运算符重载如何在 Swift 中工作。特别是,我想知道以下代码片段末尾的比较:

class Base {
    var val: Int

    init(val: Int) {
        self.val = val
    }
}

class Sub: Base {
    var subVal: Int

    init(subVal: Int, val: Int) {
        self.subVal = subVal
        super.init(val: val)
    }
}

func ==(lhs: Base, rhs: Base) -> Bool {
    return lhs.val == rhs.val
}

func ==(lhs: Sub, rhs: Sub) -> Bool {
    return lhs.val == rhs.val
        && lhs.subVal == rhs.subVal
}


let obj1 = Sub(subVal: 3, val: 4)
let obj2 = Base(val: 5)
let obj3 = Sub(subVal: 2, val: 4)
let obj4 = Base(val: 4)

obj1 == obj2            //returns false, as expected
obj1 == obj3            //returns false, as expected
obj2 == obj3            //returns false, as expected
obj1 == obj4            //returns true, as expected
obj1 == obj3 as Base    //returns true, why?
obj1 as Base == obj3    //returns true, why?
正如所指出的,我的主要问题是运行时重载运算符的行为。我希望由于 (obj3 as Base) 在运行时仍然具有 Sub 类型,因此该函数采用 lhsrhs as Sub 将被调用。然而,事实显然并非如此。有没有一种方法可以让运算符表现得像这样,而不必为 BaseSub 定义 .equals(_) 方法或类似的方法?

最佳答案

运算符作用于声明的变量的类型。尽管 Base 类型的变量可以保存 Sub 类型的对象实例,但它仍然被视为 Base。除非你告诉编译器你想将其视为其他东西(如obj3 as Base)。

在下面的代码片段中,我重用了您的类定义,稍微修改了 == 运算符,并添加了一些情况:

func ==(lhs: Base, rhs: Base) -> String {
    return "BaseCmp is \( lhs.val == rhs.val )"
}

func ==(lhs: Sub, rhs: Sub) -> String {
    return "SubCmp is \( lhs.val == rhs.val && lhs.subVal == rhs.subVal )"
}

// obj1 & obj3 is indirectly declared as Base
// obj2 & obj4 is indirectly declared as Sub
let obj1 = Sub(subVal: 3, val: 4)
let obj2 = Base(val: 5)
let obj3 = Sub(subVal: 2, val: 4)
let obj4 = Base(val: 4)

obj1 == obj2            // BaseCmp is false
obj1 == obj3            // SubCmp is false
obj2 == obj3            // BaseCmp is false
obj1 == obj4            // BaseCmp is true
obj1 == obj3 as Base    // BaseCmp is true
obj1 as Base == obj3    // BaseCmp is true

// b1, b2, b3 is directly declared as Base
// b2 & b3 holds instances of Sub
var b1 : Base = Base(val: 4)
var b2 : Base = Sub(subVal: 2, val: 4)
var b3 : Base = Sub(subVal: 8, val: 4)

// s1 directly declared as Sub, and holds a Sub
var s1 : Sub = Sub(subVal: 9, val: 4)
// s2 of type Sub, can not hold the more general Base!
// var s2 : Sub = Base(val: 4) // 'Base' is not convertible to 'Sub'

b1 == b2               // BaseCmp is true
b2 == b3               // BaseCmp is true
b2 as Sub == b3 as Sub // SubCmp is false

// Declaration and different types, BaseCmp is used
obj3 == b3          // BaseCmp is true
b2 == s1            // BaseCmp is true

// By casting the following uses SubCmp
obj3 == b3 as Sub   // SubCmp is false
obj3 == b2 as Sub   // SubCmp is true
b2 as Sub == s1   // SubCmp is false

// It is illegal to cast a Base into a Sub
// obj3 == b1 as Sub     // Execution was interrupted...

也许总结以下几点会有所帮助:

  • 如果两个变量通过声明(直接或间接)或强制转换均为 Base 类型,请使用 BaseCmp
  • 如果两个变量都是 Sub 类型(通过声明(直接或间接或强制转换)),请使用 SubCmp
  • 如果变量属于不同类型,请使用 BaseCmp 的共同点

在我的世界里,所有这些结果都是预期的,我认为不应该改变。人们可能会梦想在比较 b2 == b3 时使用最指定的共同点,因为两者实际上都拥有 Sub,但我不认为这是一个缺陷语言设计。

关于swift - Swift 中运算符重载的多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28229140/

相关文章:

ios - getCString 和 withCString 在不同架构上的行为

php - 使用 RESTful API 调用注册用户

swift - 绕过 Swift 中的 let 参数

ios - Swift Codable 空值处理

ios - 如何播放抽奖动画然后在iOS/Swift/Xcode中逐帧循环播放?

swift - 委托(delegate)在 Swift 中的 2 个 UIView 之间进行通信

swift - 如何将 Sprite 作为类添加到场景中?

ios - UIViewController 交互式转换 - 当交互式关闭被取消时,呈现的 View 消失

swift - TableView cellForRowAtIndexPath 警告

ios - 如何从子 UIViewController 到父 UIViewController 并从那里到另一个子 UIViewController