我遇到了一个问题,我希望运算符重载如何在 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
类型,因此该函数采用 lhs
和 rhs
as Sub
将被调用。然而,事实显然并非如此。有没有一种方法可以让运算符表现得像这样,而不必为 Base
和 Sub
定义 .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/