Основная проблема с equals
и hashCode
заключается в том, что есть два логических способа определения отношения эквивалентности; некоторые потребители этого класса захотят одно определение, в то время как другие потребители того же класса захотят другое.
Я бы определил два отношения эквивалентности следующим образом:
Две ссылки на объекты X и Y полностью эквивалентны, если перезапись X со ссылкой на Y не изменит текущего или будущего поведения каких-либо членов X или Y.
Две ссылки на объекты X и Y имеют эквивалентное состояние, если в программе, которая не сохранила значения, возвращенные из хеш-функции, связанной с идентификацией, замена всех ссылок на X со всеми ссылками на Y оставит состояние программы без изменений.
Обратите внимание, что второе определение в основном относится к общему сценарию, где две вещи содержат ссылки на объекты некоторого изменяемого типа (например, массивы), но могут быть уверены, что, по крайней мере, в течение определенного периода времени, представляющего интерес, эти объекты не будут подвергаться воздействию чего-либо, что может их изменить. В таком сценарии, если объекты «держателя» эквивалентны во всех других отношениях, их эквивалентность должна зависеть от того, соответствуют ли объекты, которые они содержат, определению эквивалентности second выше.
Обратите внимание, что второе определение не касается каких-либо подробностей того, как может измениться состояние объекта. Кроме того, обратите внимание, что неизменяемые объекты могут либо для определения эквивалентности сообщать о различных объектах с равным содержанием как о равных, так и неравных (если различие между X и Y и only состоит в том, что X.Equals (X) сообщает об истинном) в то время как X.Equals (Y) сообщает false, это было бы разницей, но, вероятно, было бы наиболее полезно, чтобы такие объекты использовали ссылочный идентификатор для первого отношения эквивалентности и эквивалентность других аспектов для второго.
К сожалению, поскольку Java предоставляет только одну пару классов, определяющих эквивалентность, разработчик класса должен угадать, какое определение эквивалентности будет наиболее релевантным для потребителей класса. Хотя есть существенный аргумент в пользу использования первого всегда, второе часто более практично полезно. Самая большая проблема со вторым состоит в том, что класс не может знать, когда код, использующий этот класс, захочет получить первое отношение эквивалентности.