用泛型 class 覆盖等于
Overriding equals with generic class
我创建了一个 class,它被用作 "abstract class"(仅被子classed,而不是直接实例化)。由于 Swift 不支持此功能,因此必须使用例如抽象方法主体中出现致命错误。
我的摘要 class 必须是相等的。所以我想,我在 equals 方法中使用了 fatalError:
class MySuperClass:Equatable {
}
func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
fatalError("Must override")
}
class MySubClass:MySuperClass {
let id:Int
init(_ id:Int) {
self.id = id
}
}
func ==(lhs: MySubClass, rhs: MySubClass) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass(1)
let b = MySubClass(2)
let c = MySubClass(2)
a == b
b == c
这行得通。虽然我的 subclass 有一个类型参数,但我有一个小问题。现在示例如下所示:
class MySuperClass:Equatable {
}
func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
fatalError("Must override")
}
class MySubClass<T>:MySuperClass {
let id:Int
init(_ id:Int) {
self.id = id
}
}
func ==<T>(lhs: MySubClass<T>, rhs: MySubClass<T>) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
a == b
b == c
现在它崩溃了,因为它没有 "see" 压倒一切的 equals,它只在 superclass.
中执行 equals
我知道 Swift 有一些关于使用通用类型覆盖的问题。我认为这仅限于与 obj-c 的交互。这至少看起来像是语言缺陷或错误,如果 B 是 A 的子 class,为什么泛型 class B 的等于不覆盖 class A 的等于?
在重载决议中,非泛型函数总是优先于泛型函数,因此不考虑采用 sub类 的函数先于采用 super类 的函数的次要规则。
一个可能的解决方案是使超类 ==
也通用。这样,在两个通用函数之间进行选择的规则就会生效,在这种情况下,更具体的一个是采用由 T:
参数化的特定 类 的那个
func ==<T: MySuperClass>(lhs: T, rhs: T) -> Bool {
// bear in mind, this is a question of compile-time overloading,
// rather than overriding
fatalError("Must override")
}
func ==<T>(lhs: MySubClass<T>, rhs: MySubClass<T>) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
// no longer asserts
a == b
b == c
正如 Airspeed 所暗示的那样,问题是运算符的实现不是 class/struct 实现的一部分 => 因此,继承在那里不起作用。
您可以做的是将逻辑 保留在 class 实现的 中,并让操作员使用它。例如。以下将满足您的需求:
class MySuperClass: Equatable {
func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
fatalError("Must override")
}
}
func == (lhs: MySuperClass, rhs: MySuperClass) -> Bool {
return lhs.isEqualTo(rhs)
}
class MySubClass<T>:MySuperClass {
let id: Int
init(_ id: Int) {
self.id = id
}
override func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
if let anotherSubClass = anotherSuperClass as? MySubClass<T> {
return self.id == anotherSubClass.id
}
return super.isEqualTo(anotherSuperClass) // Updated after AirSpeed remark
}
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
a == b
b == c
... 如您所见,==
运算符仅定义一次,它使用 MySuperClass
的方法来确定它的两个参数是否相等。之后 .isEqualTo()
处理其余部分,包括在 MySubClass
级别上使用继承机制。
UPD
上述方法的好处是以下方法仍然有效:
let a2: MySuperClass = a
let b2: MySuperClass = b
let c2: MySuperClass = c
a2 == b2
b2 == c2
...即无论编译时的变量类型如何,行为都将由实际实例类型决定。
我创建了一个 class,它被用作 "abstract class"(仅被子classed,而不是直接实例化)。由于 Swift 不支持此功能,因此必须使用例如抽象方法主体中出现致命错误。
我的摘要 class 必须是相等的。所以我想,我在 equals 方法中使用了 fatalError:
class MySuperClass:Equatable {
}
func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
fatalError("Must override")
}
class MySubClass:MySuperClass {
let id:Int
init(_ id:Int) {
self.id = id
}
}
func ==(lhs: MySubClass, rhs: MySubClass) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass(1)
let b = MySubClass(2)
let c = MySubClass(2)
a == b
b == c
这行得通。虽然我的 subclass 有一个类型参数,但我有一个小问题。现在示例如下所示:
class MySuperClass:Equatable {
}
func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
fatalError("Must override")
}
class MySubClass<T>:MySuperClass {
let id:Int
init(_ id:Int) {
self.id = id
}
}
func ==<T>(lhs: MySubClass<T>, rhs: MySubClass<T>) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
a == b
b == c
现在它崩溃了,因为它没有 "see" 压倒一切的 equals,它只在 superclass.
中执行 equals我知道 Swift 有一些关于使用通用类型覆盖的问题。我认为这仅限于与 obj-c 的交互。这至少看起来像是语言缺陷或错误,如果 B 是 A 的子 class,为什么泛型 class B 的等于不覆盖 class A 的等于?
在重载决议中,非泛型函数总是优先于泛型函数,因此不考虑采用 sub类 的函数先于采用 super类 的函数的次要规则。
一个可能的解决方案是使超类 ==
也通用。这样,在两个通用函数之间进行选择的规则就会生效,在这种情况下,更具体的一个是采用由 T:
func ==<T: MySuperClass>(lhs: T, rhs: T) -> Bool {
// bear in mind, this is a question of compile-time overloading,
// rather than overriding
fatalError("Must override")
}
func ==<T>(lhs: MySubClass<T>, rhs: MySubClass<T>) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
// no longer asserts
a == b
b == c
正如 Airspeed 所暗示的那样,问题是运算符的实现不是 class/struct 实现的一部分 => 因此,继承在那里不起作用。
您可以做的是将逻辑 保留在 class 实现的 中,并让操作员使用它。例如。以下将满足您的需求:
class MySuperClass: Equatable {
func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
fatalError("Must override")
}
}
func == (lhs: MySuperClass, rhs: MySuperClass) -> Bool {
return lhs.isEqualTo(rhs)
}
class MySubClass<T>:MySuperClass {
let id: Int
init(_ id: Int) {
self.id = id
}
override func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
if let anotherSubClass = anotherSuperClass as? MySubClass<T> {
return self.id == anotherSubClass.id
}
return super.isEqualTo(anotherSuperClass) // Updated after AirSpeed remark
}
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
a == b
b == c
... 如您所见,==
运算符仅定义一次,它使用 MySuperClass
的方法来确定它的两个参数是否相等。之后 .isEqualTo()
处理其余部分,包括在 MySubClass
级别上使用继承机制。
UPD
上述方法的好处是以下方法仍然有效:
let a2: MySuperClass = a
let b2: MySuperClass = b
let c2: MySuperClass = c
a2 == b2
b2 == c2
...即无论编译时的变量类型如何,行为都将由实际实例类型决定。