Каждый компетентный Java-программист знает, что вам нужно использовать String.equals () для сравнения строки, а не ==, потому что == проверяет равенство ссылок.
Когда я имею дело со строками, большую часть времени я проверяю равенство значений, а не ссылочное равенство. Мне кажется, что было бы более интуитивно понятно, если бы язык позволял сравнивать строковые значения, просто используя ==.
Для сравнения, оператор C # == проверяет равенство значений для строки s. И если вам действительно нужно проверить равенство ссылок, вы можете использовать String.ReferenceEquals.
Другим важным моментом является то, что строки являются неизменяемыми, поэтому использование этой функции не повредит.
Есть ли какая-то конкретная причина, почему это не реализовано в Java?
==
есть равенство объектов и равенствоeq
ссылок ( ofps.oreilly.com/titles/9780596155957/… ).==
оператор отображается только в томEquals
случае, если==
оператор был реализован таким образом. Поведение по умолчанию для==
такое же, какReferenceEquals
(на самом деле,ReferenceEquals
определяется как объектная версия==
)Ответы:
Я предполагаю, что это просто последовательность или «принцип наименьшего удивления». String - это объект, поэтому было бы удивительно, если бы его обрабатывали иначе, чем другие объекты.
В то время, когда появилась Java (~ 1995), просто иметь что-то подобное
String
было для большинства программистов полной роскошью, которые привыкли представлять строки в виде массивов с нулевым символом в конце.String
поведение сейчас такое, какое было тогда, и это хорошо; незначительное изменение поведения в дальнейшем может иметь неожиданные, нежелательные эффекты в рабочих программах.В качестве примечания вы можете использовать,
String.intern()
чтобы получить каноническое (интернированное) представление строки, после чего можно будет проводить сравнения==
. Стажировка занимает некоторое время, но после этого сравнения будут действительно быстрыми.Дополнение: в отличие от некоторых ответов, речь идет не о поддержке перегрузки операторов .
+
Оператор (конкатенация) работает наString
с , даже если Java не поддерживает оператор перегрузки; это просто обрабатывается как особый случай в компиляторе, разрешаясь вStringBuilder.append()
. Точно так же==
можно было бы рассматривать как особый случай.Тогда зачем удивлять особым случаем,
+
а не чем==
? Потому что+
просто не компилируется, когда применяется к не-String
объектам, так что это быстро становится очевидным. Различное поведение в==
будет гораздо менее очевидно и , таким образом , гораздо более удивительно , когда он ударяет вас.источник
Джеймс Гослинг , создатель Java, объяснил это еще в июле 2000 года :
источник
==
Оператор перегружен для объектов и примитивов.+
Оператор перегруженbyte
,short
,int
,long
,float
,double
,String
и , вероятно , несколько других , я забыл. Было бы вполне возможно перегрузку==
дляString
а.Согласованность в языке. Наличие оператора, который действует по-другому, может быть удивительным для программиста. Java не позволяет пользователям перегружать операторы - поэтому равенство ссылок является единственным разумным значением для
==
объектов.В Java:
==
сравнивает числовое равенство==
сравнивает логическое равенство==
сравнивает ссылочную идентичность.equals(Object o)
для сравнения значенийВот и все. Простое правило и просто определить, что вы хотите. Это все описано в разделе 15.21 JLS . Он состоит из трех подразделов, которые легко понять, реализовать и обдумать.
После того, как вы разрешите перегрузку
==
, точное поведение не является чем-то, что вы можете посмотреть на JLS, указать пальцем на конкретный элемент и сказать «вот как это работает», код может стать трудным для рассуждения. Точное поведение==
может быть удивительным для пользователя. Каждый раз, когда вы видите это, вы должны вернуться и проверить, что это на самом деле означает.Поскольку Java не допускает перегрузку операторов, необходимо иметь тест на равенство значений, который можно переопределить базовым определением. Таким образом, это было предписано этими вариантами дизайна.
==
в Java тестирует числовые значения для числовых типов, логическое равенство для логических типов и ссылочное равенство для всего остального (который может переопределять.equals(Object o)
все, что они хотят для равенства значений).Это не вопрос «есть ли вариант использования для конкретного последствия этого проектного решения», а скорее «это проектное решение для облегчения этих других вещей, это его следствие».
Строковое интернирование , является одним из таких примеров этого. Согласно JLS 3.10.5 все строковые литералы интернированы. Другие строки интернируются, если
.intern()
на них ссылаются . Это"foo" == "foo"
правда, является следствием дизайнерских решений, принятых для минимизации использования памяти литералами String. Кроме того, интернирование String - это то, что находится на уровне JVM и имеет небольшую открытость для пользователя, но в подавляющем большинстве случаев не должно касаться программиста (а сценарии использования для программистов не были что-то, что было в списке для дизайнеров при рассмотрении этой функции).Люди будут указывать на это
+
и+=
перегружены для строки. Однако это не здесь и не там. Остается, что если==
значение String (и только String) имеет значение равенства, то для равенства ссылок потребуется другой метод (который существует только в String). Кроме того, это излишне усложнит методы, которые принимают Object и ожидают, что==
будут вести себя одинаково и.equals()
вести себя другим, что потребует от пользователей особого случая для всех этих методов для String.Согласованный контракт для
==
объектов - это только ссылочное равенство, и оно.equals(Object o)
существует для всех объектов, которые должны проверяться на равенство значений. Усложнение этого усложняет слишком много вещей.источник
new String("foo") == new String("foo")
может быть истина (см. Дедупликация строк ).Java не поддерживает перегрузку операторов, что означает, что это
==
применимо только к примитивным типам или ссылкам. Все остальное требует вызова метода. Почему дизайнеры сделали это - вопрос, на который могут ответить только они. Если бы мне пришлось угадывать, это, вероятно, потому, что перегрузка операторов приносит сложность, которую они не были заинтересованы в добавлении.Я не эксперт в C #, но разработчики этого языка, похоже, настроили его так, что каждый примитив является a,
struct
а каждыйstruct
- объектом. Поскольку C # допускает перегрузку операторов, такая компоновка позволяет любому классу, а не простоString
заставить себя работать «ожидаемым» образом с любым оператором. C ++ допускает то же самое.источник
==
подразумевается равенство строк, нам потребуются другие обозначения для равенства ссылок.==
. Это эффективно добавляет перегрузку операторов, что будет иметь огромное влияние на способ реализации Java.ClassName.ReferenceEquals(a,b)
), а также==
оператор иEquals
метод по умолчанию, указывающие наReferenceEquals
.Это было сделано по-другому на других языках.
В Object Pascal (Delphi / Free Pascal) и C # оператор равенства определен для сравнения значений, а не ссылок, при работе со строками.
В частности, в Pascal string - это примитивный тип (одна из вещей, которые мне действительно нравятся в Pascal, получение NullreferenceException только из-за неинициализированной строки просто раздражает) и имеют семантику копирования при записи, что делает (в большинстве случаев) строковые операции очень дешево (другими словами, заметно только после того, как вы начнете объединять многомегабайтные строки).
Итак, это решение для разработки языка для Java. Когда они разрабатывали язык, они следовали пути C ++ (например, Std :: String), поэтому строки - это объекты, что является ИМХО, чтобы компенсировать отсутствие в C реального типа строк вместо того, чтобы делать строки примитивными (какими они являются).
Поэтому по причине, почему, я могу только предположить, что они сделали это, чтобы легко на их стороне и не кодируя оператор, сделать исключение на компиляторе для строк.
источник
String
должен был быть примитивный тип в Java. В отличие от других типов, компилятор должен знать оString
; кроме того, операции над ним будут достаточно распространены, так что для многих типов приложений они могут создавать узкое место в производительности (что может быть облегчено встроенной поддержкой). Типичныйstring
[нижний регистр] будет иметь объект, выделенный в куче для хранения его содержимого, но никакой «нормальной» ссылки на этот объект не будет нигде; таким образом, это может быть однонаправленноеChar[]
илиByte[]
не обязательно бытьChar[]
косвенным через другой объект.В Java нет никакой перегрузки операторов, и поэтому операторы сравнения перегружаются только для примитивных типов.
Класс «String» не является примитивом, поэтому он не имеет перегрузки для «==» и использует по умолчанию сравнение адреса объекта в памяти компьютера.
Я не уверен, но я думаю , что в Java 7 или 8 оракул сделал исключение в компиляторе , чтобы признать в
str1 == str2
качествеstr1.equals(str2)
источник
s1
иs2
и дать им такое же содержание, они проходят равенство (s1.equals(s2)
сравнение) , но не такое же ссылка (==
) для сравнения , потому что они два разных объекта. Изменение семантики==
для обозначения равенства приведетs1 == s2
к оценке того,true
где она использовалась для оценкиfalse
.Похоже, что Java была разработана, чтобы поддерживать фундаментальное правило, согласно которому
==
оператор должен быть допустимым в любое время, когда один операнд может быть преобразован в тип другого, и должен сравнивать результат такого преобразования с не преобразованным операндом.Это правило едва ли уникально для Java, но оно имеет некоторые далеко идущие (и, к сожалению, IMHO) эффекты на разработку других связанных с типом аспектов языка. Было бы чётче указывать поведение в
==
отношении определенных комбинаций типов операндов и запрещать комбинации типов X и Y, гдеx1==y1
иx2==y1
не подразумевалось быx1==x2
, но языки редко делают это [в соответствии с этой философией,double1 == long1
либо нужно было бы указать,double1
не является точным представлениемlong1
или отказывается компилировать;int1==Integer1
должно быть запрещено, но должны быть удобные и эффективные, не бросающие средства, средства проверки, является ли объект целым в штучной упаковке с определенным значением (сравнение с чем-то, что не является целым в штучной упаковке, должно просто возвращатьсяfalse
)].Что касается применения
==
оператора к строкам, если бы Java запретила прямые сравнения между операндами типаString
иObject
, она вполне могла бы избежать неожиданностей в поведении==
, но не было бы такого поведения, которое она могла бы реализовать для таких сравнений, которые не были бы удивительными. Наличие двух строковых ссылок, сохраняемых в типе,Object
ведет себя иначе, чем ссылки, хранящиеся в типе,String
было бы гораздо менее удивительно, чем если бы любое из этих поведений отличалось от такового при законном сравнении смешанного типа. ЕслиString1==Object1
это законно, это будет означать, что единственный способ поведенияString1==String2
иObject1==Object2
сопоставленияString1==Object1
состоит в том, чтобы они соответствовали друг другу.источник
==
для объектов должен просто вызывать (null-safe) equals и что-то еще (например,===
илиSystem.identityEqual
) должно использоваться для сравнения идентификаторов. Смешивание примитивов и объектов изначально было бы запрещено (до 1.5 не было автобокса), а затем можно было бы найти какое-то простое правило (например, unll-safe unbox, затем приведение, затем сравнение).int==Integer
оператор return,false
если значениеInteger
равно нулю, и иначе сравнивать значения, этот подход был бы непохожим на поведение==
во всех других обстоятельствах, где он безоговорочно приводит оба операнда к одному и тому же типу до сравнивая их. Лично мне интересно, было ли введено автоматическое распаковывание, чтобы позволитьint==Integer
поведение, которое не было бессмысленным ...int
и сравнение ссылок было бы глупо [но не всегда терпело неудачу]. В противном случае я не вижу причин, позволяющих неявное преобразование, которое может завершиться неудачей с NPE.==
не имеет ничего общегоidentityEquals
. +++ "отдельные операторы равенства для равенства значений и ссылок" - но какие? Я бы рассмотреть как примитивные==
иequals
как делать значение сравнения в том смысле , чтоequals
смотрит на значении ссылки. +++ Если==
подразумеваетсяequals
, тогдаint==Integer
СЛЕДУЕТ делать автобокс и сравнивать ссылки с использованием нулевых безопасных равных. +++ Боюсь, моя идея на самом деле не моя, а только то, что делает Котлин.==
никогда не проверялось равенство ссылок, то он мог бы разумно выполнить нулевой безопасный тест на равенство значений. Тот факт , что она делает тест равенства ссылок, однако, сильно ограничивает , как он может обрабатывать смешанные сравнения ссылки / значения без непоследовательности. Также обратите внимание, что в Java зафиксировано представление о том, что операторы переводят оба операнда в один и тот же тип, а не приводят к особому поведению, основанному на комбинациях задействованных типов. Например,16777217==16777216.0f
возвращает,true
потому что выполняет преобразование с потерями первого операнда вfloat
, в то время как ...В целом, есть очень веская причина хотеть иметь возможность проверить, указывают ли две ссылки на один и тот же объект. У меня было много раз, что я написал
Я могу иметь или не иметь функцию равенства в таких случаях. Если я это сделаю, функция equals может сравнить все содержимое обоих объектов. Часто это просто сравнивает некоторый идентификатор. «A и B - это ссылки на один и тот же объект», а «A и B - это два разных объекта с одинаковым содержанием», это, конечно, две совершенно разные идеи.
Вероятно, это правда, что для неизменяемых объектов, таких как строки, это не проблема. С неизменными объектами мы склонны думать об объекте и ценности как об одном и том же. Ну, когда я говорю «мы», я имею в виду, по крайней мере, «я».
Конечно, это возвращает ложь, но я вижу, что кто-то думает, что это должно быть правдой.
Но как только вы скажете, что == сравнивает дескрипторы ссылок, а не содержимое объектов в целом, создание особого случая для строк может привести к путанице. Как сказал кто-то еще здесь, что, если вы хотите сравнить дескрипторы двух объектов String? Будет ли какая-то специальная функция, чтобы сделать это только для строк?
И что насчет ...
Это ложно, потому что это два разных объекта, или истина, потому что это строки, содержимое которых одинаково?
Так что да, я понимаю, как программисты смущаются этим. Я сделал это сам, я имею в виду написать if myString == "foo", когда я имел в виду myString.equals ("foo"). Но, за исключением изменения смысла оператора == для всех объектов, я не вижу, как его решить.
источник
==
для обозначения «равных строк».==
, как вы упоминали в конце.==
было подвержено ошибкам.Это действительно вопрос для
Strings
, а не только для строк, но и для других неизменных объектов , представляющих некоторые «ценность», напримерDouble
,BigInteger
и дажеInetAddress
.Чтобы сделать
==
оператор пригодным для использования со строками и другими классами значений, я вижу три варианта:Пусть компилятор узнает обо всех этих классах-значениях и способах сравнения их содержимого. Если бы это было всего несколько классов из
java.lang
пакета, я бы это учел, но это не распространяется на такие случаи, как InetAddress.Разрешить перегрузку операторов, чтобы класс определял
==
поведение при сравнении.Удалите открытые конструкторы и используйте статические методы, возвращающие экземпляры из пула, всегда возвращающие один и тот же экземпляр для одного и того же значения. Чтобы избежать утечек памяти, вам нужно что-то вроде SoftReferences в пуле, которого не было в Java 1.0. И теперь, чтобы сохранить совместимость,
String()
конструкторы больше не могут быть удалены.Единственное, что еще можно сделать сегодня, это ввести перегрузку операторов, и лично я не хотел бы, чтобы Java пошла по этому пути.
Для меня читаемость кода является наиболее важной, и программист на Java знает, что операторы имеют фиксированное значение, определенное в спецификации языка, тогда как методы определены в некотором коде, и их значение нужно искать в Javadoc метода. Я хотел бы остаться с этим различием, даже если это означает, что сравнения строк не смогут использовать
==
оператор.Есть только один аспект сравнений Java, который меня раздражает: эффект автобокса и -бокса. Это скрывает различие между примитивом и типом обертки. Но когда вы сравниваете их
==
, они ОЧЕНЬ разные.источник