Почему Java не предупреждает о == "что-то"? - PullRequest
12 голосов
/ 14 мая 2010

Это может звучать глупо, но почему компилятор Java не предупреждает о выражении в следующем выражении if:

String a = "something";
if(a == "something"){
  System.out.println("a is equal to something");
}else{
  System.out.println("a is not equal to something");
}

Я понимаю, почему выражение не соответствует действительности, но AFAIK, a никогда не может быть равен строковому литералу «что-то». Компилятор должен понять это и, по крайней мере, предупредить меня, что я идиот, который кодирует путь поздно ночью.

Разъяснение Этот вопрос не о сравнении двух переменных объекта String, а о сравнении переменной объекта String с литералом String. Я понимаю, что следующий код полезен и даст результаты, отличные от .equals():

String a = iReturnAString();
String b = iReturnADifferentString();
if(a == b){
  System.out.println("a is equal to b");
}else{
  System.out.println("a is not equal to b");
}

В этом случае a и b могут фактически указывать на одну и ту же область в памяти, даже если это не из-за интернирования. В первом примере, единственная причина, по которой это было бы верно, заключается в том, что Java делает что-то закулисное, что мне не полезно и которое я не могу использовать в своих интересах.

Контрольный вопрос Даже если a и строковый литерал указывают на одну и ту же область памяти, как это полезно для меня в выражении, подобном приведенному выше. Если это выражение вернет истину, я ничего не смогу сделать с этим знанием, не так ли? Если бы я сравнивал две переменные, то да, эта информация была бы полезной, но с переменной и литералом это вроде бы бессмысленно.

Ответы [ 9 ]

13 голосов
/ 14 мая 2010

На самом деле они действительно могут быть одной и той же ссылкой, если Java решит интернировать строку. Строковое интернирование - это понятие наличия только одного значения для отдельной строки во время выполнения.

Java-заметки о интернировании строк

8 голосов
/ 14 мая 2010

Предупреждения компилятора, как правило, относятся к вещам, которые либо явно неверны (условные выражения, которые никогда не могут быть истинными или ложными), либо небезопасны (непроверенные приведения). Использование == допустимо, а в некоторых редких случаях является преднамеренным.

Я полагаю, что все Checkstyle , FindBugs и PMD предупредят об этом, и, возможно, многие другие плохие практики, которые мы склонны иметь, когда наполовину спим или иным образом недееспособным;).

4 голосов
/ 14 мая 2010

Потому что:

  • Вы можете использовать ==, если работаете с константами и интернированными строками
  • компилятор должен сделать исключение только для String, а не для другого типа. Я имею в виду, что всякий раз, когда компилятор встречает ==, он должен проверить, являются ли операнды Strings, чтобы выдать предупреждение. Что если аргументы Strings, но называются Object или CharSequence?

Обоснование, данное checkstyle для выдачи ошибки, заключается в том, что начинающие программисты часто делают это. И если вы новичок, мне будет сложно настроить стиль проверки (или pmd) или даже узнать о них.

Другая вещь - это реальный сценарий, когда строки сравниваются, и в качестве одного из операндов используется литерал. Во-первых, было бы лучше использовать константу (static final) вместо литерала. И откуда взялся другой операнд? Вполне вероятно, что он будет происходить из той же константы / литерала, где-то еще в коде. Так что == будет работать.

3 голосов
/ 14 мая 2010

В зависимости от контекста, сравнение идентичности и сравнение значений могут быть законными.

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

Следовательно, нет попытки сделать это автоматически.

Если вы думаете о таких вещах, как кэширование, то существуют ситуации, когда вы захотите провести этот тест.

2 голосов
/ 14 мая 2010

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

1 голос
/ 15 мая 2010

«Я понимаю, почему выражение не соответствует действительности, но AFAIK, a никогда не может быть равен строковому литералу« что-то ».»

Чтобы уточнить, в приведенном примере выражение всегда TRUE, а a может быть == и равно () литералу String, а в данном примере оно всегда == и равно () .

Ирония в том, что вы, кажется, привели редкий контрпример к вашему собственному аргументу.

1 голос
/ 14 мая 2010

Компилятору все равно, что вы пытаетесь сравнить идентичность с литералом. Можно также утверждать, что не работа компилятора - быть няней кода. Ищите инструмент, похожий на ворс, если вы хотите ловить подобные ситуации.

0 голосов
/ 14 мая 2010

Существуют инструменты, которые будут предупреждать вас об этих конструкциях; не стесняйтесь использовать их. Однако существуют допустимые случаи, когда вы хотите использовать == в строках, и языковой дизайн гораздо хуже предупредить пользователя о совершенно правильной конструкции, чем не предупредить его. Когда вы используете Java год или около того (и я готов поспорить, что вы еще не достигли этой стадии), вы обнаружите, что избегаете конструкций, как это вторая натура.

0 голосов
/ 14 мая 2010

В некоторых случаях вам действительно важно, имеете ли дело с одним и тем же объектом, а не равны ли два объекта. В таких случаях вам нужно == вместо equals(). Компилятор не может узнать, действительно ли вы хотите сравнить ссылки на равенство или объекты, на которые они указывают.

Теперь гораздо менее вероятно, что вы захотите == для строк, чем для пользовательского типа, но это не гарантирует, что вы этого не захотите, и даже если это Сделано, это означает, что компилятор должен был бы проверить строки специального случая, чтобы убедиться, что вы не используете == для них.

Кроме того, поскольку строки являются неизменяемыми, JVM может создавать строки, которые будут равны для equals() общего ресурса для одного и того же экземпляра (для экономии памяти), и в этом случае они также будут равны для ==. Таким образом, в зависимости от того, что делает JVM, == вполне может вернуть true. И пример, который вы привели, на самом деле дает хороший шанс, потому что они оба строковые литералы, поэтому для JVM было бы довольно легко сделать их одной и той же строкой, и, вероятно, это произойдет. И, конечно, если вы хотите увидеть, создает ли JVM две строки для одного и того же экземпляра, вы бы имели , чтобы использовать == вместо equals(), поэтому есть законная причина хотеть используйте == прямо на строках.

Итак, компилятор не может знать достаточно того, что вы делаете, чтобы знать, что использование == вместо equals() должно быть ошибкой. Это может привести к ошибкам, если вы не будете осторожны (особенно если вы привыкли к языку, подобному C ++, который перегружает == вместо отдельной функции equals()), но компилятор может сделать для вас только очень многое. Есть законные причины для использования == вместо equals(), поэтому компилятор не будет отмечать это как ошибку.

...