Ответ 1
Причина, по которой вызывается равенство A
для Array<A>
, содержащего B
, заключается в том, что перегрузка свободных функций разрешена статически, а не динамически - то есть во время компиляции на основе типа, а не при времени выполнения, основанного на значении, указанном на рисунке.
Это неудивительно, что ==
не объявляется внутри класса, а затем переопределяется в подклассе. Это может показаться очень ограниченным, но, честно говоря, определение полиморфного равенства с использованием традиционных методов ОО чрезвычайно (и обманчиво) затруднено. Подробнее см. эту ссылку и этот документ.
Наивное решение может состоять в том, чтобы определить динамически отправленную функцию в A
, затем определить ==
, чтобы просто вызвать это:
class A: Equatable {
func equalTo(rhs: A) -> Bool {
// whatever equality means for two As
}
}
func ==(lhs: A, rhs: A) -> Bool {
return lhs.equalTo(rhs)
}
Затем, когда вы реализуете B
, youd переопределяет equalTo
:
class B: A {
override func equalTo(rhs: A) -> Bool {
return (rhs as? B).map { b in
return // whatever it means for two Bs to be equal
} ?? false // false, assuming a B and an A can’t be Equal
}
}
Вам еще нужно сделать один танец as?
, потому что вам нужно определить, является ли правый аргумент B
(если equalTo
взял B
напрямую, это не было бы законным переопределением).
Здесь также все еще возможно удивительное поведение:
let x: [A] = [B()]
let y: [A] = [A()]
// this runs B’s equalTo
x == y
// this runs A’s equalTo
y == x
То есть порядок аргументов изменяет поведение. Это нехорошо - люди ожидают, что равенство будет симметричным. Так что вам действительно нужны некоторые из методов, описанных в ссылках выше, чтобы решить это правильно.
В этот момент вы можете почувствовать, что все это становится ненужным. Вероятно, это особенно важно, учитывая следующий комментарий в документации для Equatable
в стандартной библиотеке Swift:
Равенство подразумевает подставляемость. Когда
x == y
,x
иy
взаимозаменяемы в любом коде, который зависит только от их значений.Идентификатор экземпляра класса, отличающийся тройным эквивалентом
===
, равен в частности, не является частью значения экземпляра. Предоставление других нецензурных аспекты типовEquatable
обескуражены, а любые, которые должны быть четко указаны в документации.
Учитывая это, вы можете серьезно подумать о том, чтобы получить представление о вашей реализации Equatable
, если способ реализации равенства не в том смысле, что вы довольны тем, что два значения равны друг другу. Один из способов избежать этого состоит в том, чтобы рассматривать идентификацию объекта как меру равенства и реализовывать ==
в терминах ===
, который нужно выполнить только один раз для суперкласса. Кроме того, вы можете спросить себя, действительно ли вам нужно наследование реализации? И если нет, подумайте о том, чтобы отбросить его и использовать значения типов вместо этого, а затем использовать протоколы и генерические средства для захвата полиморфного поведения, которое вы ищете.