Scala, Java и равенство

val filesHere = (new java.io.File(".")).listFiles
val filesHere2 = (new java.io.File(".")).listFiles

scala> filesHere == filesHere2
res0: Boolean = false

Это довольно нелогично. Я бы предпочел, чтобы файлы filesHere и filesHere2 были равны.

Это, безусловно, связано с несоответствием семантики между Java и Scala, например, в отношении массивов или равенства (файлов). Очевидно, я что-то упускаю здесь!


person acherm    schedule 06.08.2010    source источник


Ответы (4)


Вы можете прочитать здесь:

http://ofps.oreilly.com/titles/9780596155957/AdvancedObjectOrientedProgramming.html#EqualityOfObjects

но похоже, что если вы сделали: filesHere.sameElements(filesHere2) это должно быть правдой.

Javadoc для этого находится здесь: http://www.scala-lang.org/api/2.6.0/scala/IterableProxy.html#sameElements%28Iterable%5BB%5D%29

ОБНОВЛЕНИЕ:

Несколько фрагментов из первой ссылки, которые могут быть полезны:

В Java, C++ и C# оператор == проверяет ссылку, а не равенство значений. Напротив, оператор Ruby == проверяет равенство значений. К какому бы языку вы ни привыкли, обязательно помните, что в Scala == проверяет равенство значений.

Что касается ==, который не работает должным образом в списках:

Хотя это может показаться несоответствием, поощрение явной проверки равенства двух изменяемых структур данных является консервативным подходом со стороны разработчиков языка. В долгосрочной перспективе это должно уберечь вас от неожиданных результатов в ваших условных выражениях.

ОБНОВЛЕНИЕ 2:

Основываясь на комментариях Рафаэля, я просмотрел спецификацию, и она была датирована двумя днями ранее для самого последнего обновления, и я вижу их по адресу http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf:

Method equals: (Any)Boolean is structural equality, where two instances
are equal if they both belong to the case class in question and they have equal
(with respect to equals) constructor arguments.

class AnyRef extends Any {
    def equals(that: Any): Boolean = this eq that
    final def eq(that: AnyRef): Boolean = . . . // reference equality

Итак, похоже, что определение не изменилось в Scala 2.10.2, поскольку спецификация кажется последовательной. Если поведение отличается, то если вы пишете модульный тест, его следует отправить как ошибку для Scala.

person James Black    schedule 06.08.2010
comment
Хороший ресурс, спасибо! Он показывает, как решить проблему, упомянутую выше (используя те же элементы, хорошо!), но не объясняет, почему возникает проблема (или неожиданность), как в scala › Array(1, 2) == Array(1, 2) res0 : логическое значение = ложь - person acherm; 06.08.2010
comment
@acherm - Вот почему я добавил обновление, последняя цитата из статьи должна объяснять, почему, чтобы поощрять явное тестирование на равенство. - person James Black; 06.08.2010
comment
Еще одно интересное обсуждение здесь: scalide.blogspot.com /2009/05/ Это не полностью проясняет ситуацию, но показывает, что равенство является интересной темой просто из-за невероятного разнообразия его интерпретаций. Глядя на Array.scala (lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_8_0_final/src/), метод equals() не определен. - person acherm; 06.08.2010
comment
Это не совсем верно в отношении C#, так как == может быть и часто переопределяется для ссылочных типов, чтобы означать равенство значений. Object.ReferenceEquals всегда означает ссылочное равенство. - person denis phillips; 08.08.2010
comment
@dpp - Когда вы переопределяете операторы, можно делать любые предположения. Если я могу заставить «+» выполнять вычитание, это не означает, что какие-либо предположения относительно оператора «плюс» неверны. - person James Black; 08.08.2010
comment
@James Black - Вы, конечно, правы в том, что перегрузка оператора, выполненная плохо (что, вероятно, происходит в большинстве случаев), не может вызвать ничего, кроме неприятностей. Я хотел сказать, что даже в BCL есть много мест, где == переопределяется, чтобы делать правильные вещи, и, на мой взгляд, в этом аспекте он имеет больше общего со Scala, чем с Java. - person denis phillips; 08.08.2010
comment
Ссылка была перемещена: ofps.oreilly.com/titles/9780596155957/ - person Nikolaos; 28.12.2012
comment
Семантика equals и ==, кажется, изменилась (обратно к стилю Java) с последними обновлениями. Можешь подтвердить? - person Raphael; 15.08.2013
comment
@Raphael - судя по этим двум ссылкам, похоже, что он не изменился: programmers.stackexchange.com/questions/193638/, blog.scala4java.com/2013/05/scala-equality-in-30-seconds.html - person James Black; 16.08.2013
comment
@JamesBlack Я просматривал API версии 2.10.2. который является более свежим, чем вопрос в разделе Software Engineering, на который вы ссылаетесь. По крайней мере, я читаю равенство как один и тот же объект, а эквивалентность — как одно и то же значение. Некоторые мини-тесты, которые я провел с другом, сбивали с толку (перезапись equals, похоже, влияет на вызовы как equals, так и ==), поэтому я решил спросить. Как вы думаете, я должен открыть правильный вопрос? (Вторая ссылка у меня не работает.) - person Raphael; 16.08.2013
comment
@Raphael - я только что обновил свой ответ, но, судя по спецификации, мой ответ все еще согласуется. - person James Black; 17.08.2013
comment
@JamesBlack Спасибо. Я до сих пор не понимаю, как == вписывается в картину, и как перезапись equals меняет оба, даже если они имеют разные спецификации. Ведут ли себя классы case иначе, чем обычные классы? - person Raphael; 17.08.2013
comment
@Raphael. Если вы посмотрите на scala-lang.org/old/node/258 вы увидите, что функция equals в классах case переопределена для автоматического сравнения структур (значений). На самом деле я не понимаю, как равные могут изменить оба, но этот ответ может оказаться полезным: stackoverflow.com/a/7681243/ 67566 - person James Black; 17.08.2013
comment
Спасибо, это помогло. Кажется, мы совсем пропустили eq. - person Raphael; 18.08.2013

Если бы я правил миром, я бы отказался от метода eq в Scala на том основании, что его имя чрезвычайно путается с равными и ==. Вместо этого в английском языке есть слово, которое выражает тождество, а не равенство: я бы назвал его просто is.

Точно так же я заменил бы ne в Scala (ужасное имя, поскольку оно одновременно и аббревиатура, и непонятное) на isnt .

Мне кажется, что их действительно можно добавить в AnyRef, а старые методы устарели даже на этом позднем этапе.

person Jonathan    schedule 06.08.2010
comment
Да, хорошая идея, но это неоднозначно с проверками типа, например: obj is String, который популярен в других языках и, таким образом, сбивает с толку. - person tuxSlayer; 04.08.2011

Метод equals() для массивов Java использует равенство ссылок, а не что-то более сложное, а == в Scala — это просто equals() в Java.

person Tom Crockett    schedule 06.08.2010
comment
В Scala == и equals() используются для равенства значений, а eq указывает, имеют ли два объекта равенство ссылок. - person James Black; 06.08.2010
comment
@James да, но в этом случае equals() реализован для простой проверки равенства ссылок. Итак, в этом конкретном случае (Scala ==) == (Java ==) :) - person Tom Crockett; 06.08.2010
comment
Вот интересная тема: scala-programming- language.1934581.n4.nabble.com/ Но все же, я действительно запутался. Если Scala == (или equals) на самом деле вызывает метод equals() Java (как кажется), этот пример несколько нарушает выбор дизайна языка Scala в отношении равенства. Как Scala-программист, я должен напоминать о семантике равенства в Java и быть осторожным со всеми хитрыми моментами. Черт! - person acherm; 06.08.2010
comment
Вы не одиноки в том, что смущены этим... это сбило с толку многих незадачливых новичков в Scala. Вы хоть понимаете, что происходит? - person Tom Crockett; 06.08.2010
comment
Кроме того, многие люди, занимающиеся Java, говорят, что если вы не имеете дело с устаревшим API, который заставляет вас работать, не используйте массивы, используйте коллекции. Коллекции имеют сильную семантику equals(). - person Thomas Dufour; 06.08.2010

Сравнение не работает должным образом, поскольку этот Java API возвращает массив.

Массивы Scala и массивы Java за кулисами одинаковы, и хотя массив Scala выглядит как класс, это всего лишь java.io.File[] (в этом примере).

По этой причине проверка на равенство не может быть отменена. Scala должна использовать для этого семантику Java.

Рассмотрим этот пример:

val filesHere = (new java.io.File(".")).listFiles.toList
val filesHere2 = (new java.io.File(".")).listFiles.toList

Это будет работать, как и ожидалось.

person soc    schedule 06.08.2010
comment
@FUD да, потому что это массив. Но если вы позвоните .toList -- он начнет работать. Это потому, что он станет классом из мира Scala, а классы в Scala определяются с помощью более удобного equals. - person VasiliNovikov; 02.09.2014