Некоторое время я размышлял, почему Java и C # (и я уверен, что другие языки) по умолчанию ссылаются на равенство для ==
.
В программировании, которое я делаю (что, безусловно, является лишь небольшим подмножеством проблем программирования), я почти всегда хочу логическое равенство при сравнении объектов вместо ссылочного равенства. Я пытался придумать, почему оба эти языка пошли по этому пути, вместо того, чтобы инвертировать его и ==
иметь логическое равенство и использовать .ReferenceEquals()
для ссылочного равенства.
Очевидно, что использование равенства ссылок очень просто для реализации и дает очень последовательное поведение, но не похоже, что оно хорошо согласуется с большинством практик программирования, которые я вижу сегодня.
Я не хочу казаться неосведомленным о проблемах с попыткой осуществить логическое сравнение, и что это должно быть реализовано в каждом классе. Я также понимаю, что эти языки были разработаны давно, но общий вопрос остается в силе.
Есть ли какое-то главное преимущество по умолчанию в том, что я просто упускаю это, или кажется разумным, что поведение по умолчанию должно быть логическим равенством, а возвращение к ссылочному равенству, если для класса не существует логического равенства?
Equals()
, это не меняет автоматически поведение==
?Ответы:
C # делает это, потому что Java сделал. Java сделала, потому что Java не поддерживает перегрузку операторов. Поскольку равенство значений должно быть переопределено для каждого класса, оно не может быть оператором, а должно быть методом. ИМО это было плохое решение. Писать и читать намного легче,
a == b
чемa.equals(b)
, и гораздо более естественно для программистов с опытом работы на C или C ++, ноa == b
это почти всегда неправильно. Ошибки от использования==
там, где.equals
требовалось, потратили бесчисленные тысячи часов программиста.источник
==
для многих классов, и несколько месяцев назад я обнаружил, что несколько разработчиков не знали, что на==
самом деле делает. Всегда существует такой риск, когда семантика некоторой конструкции не очевидна.equals()
Обозначение говорит мне , что я использую специальный метод , и что я должен искать его где - нибудь. Итог: я думаю, что перегрузка операторов - это вообще открытый вопрос.+
например, что делает сложение (числовых значений) и объединение строк одновременно.a == b
быть более естественным для программистов с опытом работы с C, так как C не поддерживает пользовательскую перегрузку операторов? (Например, C-способ сравнения строкstrcmp(a, b) == 0
, а неa == b
.)char *
. Мне кажется очевидным, что сравнение двух указателей на равенство - это не то же самое, что сравнение строк.Краткий ответ: последовательность
Однако, чтобы ответить на ваш вопрос правильно, я предлагаю сделать шаг назад и посмотреть на вопрос, что означает равенство в языке программирования. Существует как минимум ТРИ различных варианта, которые используются на разных языках:
Эти три типа равенства часто используются, потому что они удобны для реализации: все три проверки на равенство могут быть легко сгенерированы компилятором (в случае глубокого равенства, компилятору может понадобиться использовать биты тега для предотвращения бесконечных циклов, если структура сравнивать имеет круговые ссылки). Но есть другая проблема: ни один из них не может быть уместным.
В нетривиальных системах равенство объектов часто определяется как нечто между глубоким и эталонным равенством. Чтобы проверить, хотим ли мы считать два объекта равными в определенном контексте, нам может потребоваться сравнить некоторые атрибуты по тому, как они находятся в памяти, а другие по глубокому равенству, в то время как некоторым атрибутам может быть разрешено быть чем-то совершенно другим. Что нам действительно нужно, так это «четвертый тип равенства», действительно хороший, часто называемый в литературе семантическим равенством . Вещи равны, если они равны, в нашей области. знак равно
Итак, мы можем вернуться к вашему вопросу:
Что мы имеем в виду, когда пишем «a == b» на любом языке? В идеале всегда должно быть одно и то же: семантическое равенство. Но это невозможно.
Одним из основных соображений является то, что, по крайней мере для простых типов, таких как числа, мы ожидаем, что две переменные равны после присвоения одного и того же значения. Увидеть ниже:
В этом случае мы ожидаем, что «a равно b» в обоих утверждениях. Все остальное было бы безумием. Большинство (если не все) языков придерживаются этого соглашения. Следовательно, с помощью простых типов (или значений) мы знаем, как достичь семантического равенства. С объектами это может быть что-то совершенно другое. Увидеть ниже:
Мы ожидаем, что первое «если» будет всегда верным. Но что вы ожидаете от второго «если»? Это действительно зависит. Может ли DoSomething изменить (семантическое) равенство a и b?
Проблема с семантическим равенством состоит в том, что он не может быть автоматически сгенерирован компилятором для объектов, и это не очевидно из назначений . Для пользователя должен быть предусмотрен механизм определения семантического равенства. В объектно-ориентированных языках этот механизм является унаследованным методом: равно . Читая фрагмент ОО-кода, мы не ожидаем, что метод будет иметь одинаковую точную реализацию во всех классах. Мы привыкли к наследованию и перегрузкам.
Однако с операторами мы ожидаем того же поведения. Когда вы видите «a == b», вы должны ожидать одинаковый тип равенства (из 4 выше) во всех ситуациях. Таким образом, стремясь к согласованности, разработчики языков использовали равенство ссылок для всех типов. Это не должно зависеть от того, переопределил ли программист метод или нет.
PS: язык Dee немного отличается от языка Java и C #: оператор equals означает поверхностное равенство для простых типов и семантическое равенство для пользовательских классов (с ответственностью за реализацию операции =, лежащей у пользователя - по умолчанию не предусмотрено). Так как для простых типов неглубокое равенство - это всегда семантическое равенство, язык является последовательным. Однако цена, которую он платит, заключается в том, что оператор equals по умолчанию не определен для пользовательских типов. Вы должны реализовать это. И иногда это просто скучно.
источник
When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations.
Разработчики языка Java использовали ссылочное равенство для объектов и семантическое равенство для примитивов. Для меня не очевидно, что это было правильное решение или что это решение является более "последовательным", чем разрешение==
быть перегруженным для семантического равенства объектов.a
и одинb
и тот же тип, выражениеa==b
проверяетa
иb
удерживает ли одно и то же. Если один из них содержит ссылку на объект № 291, а другой содержит ссылку на объект № 572, они не содержат одно и то же. В содержание объектно # 291 и # 572 могут быть эквивалентны, но сами переменные содержат разные вещи.a == b
и знать, что он делает. Кроме того, вы можете увидетьa.equals(b)
и предположить, что перегрузкиequals
. Еслиa == b
звонкиa.equals(b)
(если реализованы), это сравнение по ссылке или по содержанию? Не помнишь? Вы должны проверить класс А. Код уже не так быстро читается, если вы даже не уверены, что вызывается. Было бы так, как если бы методы с одинаковой сигнатурой были разрешены, а вызываемый метод зависит от текущей области видимости. Такие программы было бы невозможно прочитать.Потому что последний подход будет сбивать с толку. Рассмотреть возможность:
Должен ли этот код печататься
"ok"
, или он должен броситьNullPointerException
?источник
Для Java и C # преимущество заключается в том, что они объектно-ориентированы.
С точки зрения производительности - более простой для написания кода также должен быть быстрее: поскольку ООП предполагает, что логически различные элементы должны быть представлены разными объектами, проверка равенства ссылок будет выполняться быстрее, учитывая, что объекты могут стать довольно большими.
С логической точки зрения - равенство объекта с другим не должно быть столь же очевидным, как сравнение свойств объекта на равенство (например, как логически интерпретируется null == null? Это может отличаться от случая к случаю).
Я думаю, что это сводится к тому, что вы наблюдаете, что «вы всегда хотите логического равенства, а не ссылочного равенства». Консенсус среди языковых дизайнеров был, вероятно, противоположным. Мне лично трудно это оценить, так как мне не хватает широкого спектра опыта программирования. Грубо говоря, я больше использую ссылочное равенство в алгоритмах оптимизации, а логическое равенство - больше в обработке наборов данных.
источник
.equals()
сравнивает переменные по их содержанию. вместо==
этого сравнивает объекты по их содержанию ...использование объектов более точное использование
.equals()
источник