As a minor improvement, we could move the type check type(of: self) == type(of: other) to the static == function.
This would allow us, to override equals in the subclass and call super.equals.
By doing this, we don't need to check all the properties of the superclass in our Subclass.equals function.
The next problem with this approach is though, that we now have an isEqual function, that cannot be made private (otherwise the subclass would not be able to override it).
The function does not check the type itself and therefore potentially crashes, when being called with a parameter, that does not have the right type.
But since we, the programmer, are the only person that may be using the internal isEqual function, that feels like a smaller drawback than copy-pasting the equals checks, in my opinion.
Alternatively we could define all super- and subclasses in the same file and make the isEquals function fileprivate. (see the link below)
Example: see below or here for the updated version using fileprivate and extensions.
class Vehicle: Equatable {
let name: String
init(name: String) {
self.name = name
}
func isEqual(to other: Vehicle) -> Bool {
return self.name == other.name
}
static func == (lhs: Vehicle, rhs: Vehicle) -> Bool {
return type(of: lhs) == type(of: rhs) && lhs.isEqual(to: rhs) // If the types mismatch, lhs.isEqual(to:) will never be called.
}
}
class Car: Vehicle {
let seats: Int
init(name: String, seats: Int) {
self.seats = seats
super.init(name: name)
}
// This function will only be called, after checking, that self and other have the same type, so we can assume, that other is of type Car too.
override func isEqual(to other: Vehicle) -> Bool {
assert(type(of: other) == Car.self, "`other` does not have the same type as `self`. The static function `==` should have prevented that.")
let otherCar = other as! Car
return super.isEqual(to: other) && self.seats == otherCar.seats
}
}
class Bicycle: Vehicle {
let color: String
init(name: String, color: String) {
self.color = color
super.init(name: name)
}
// This function will only be called, after checking, that self and other have the same type, so we can assume, that other is of type Car too.
override func isEqual(to other: Vehicle) -> Bool {
assert(type(of: other) == Bicycle.self, "`other` does not have the same type as `self`. The static function `==` should have prevented that.")
let otherBicycle = other as! Bicycle
return super.isEqual(to: other) && self.color == otherBicycle.color
}
}