Comments 33
UFO just landed and posted this here
Достаточно было сделать compile-time generics :)
Проблема в том, что я этих выкрутасов тоже не видел, пока FindBugs не заюзал, но при этом они были (один из них даже я сам и породил). Они жили в коде пару лет, и их никто не замечал :-)
Вы используете compareTo для проверки совпадения значений? В общем случае, не с числами, он может вам неприятно удивить.
UFO just landed and posted this here
Видимо, имеется ввиду, что compareTo может быть не согласован с equals, хотя в документации настоятельно рекомендуется делать его согласованным.
На счет double я всегда знал что этот тип сравнивать обычным способом нельзя, всегда нужно учитывать погрешность, не только в Java, но и во многих других языках.
Зависит от задачи и от ситуации. Может оказаться, например, что значение в переменную double попадает прямиком из пользовательского ввода (а не в результате какого-либо математического действия) и, скажем значение 1.0 надо по алгоритму обработать специально. Тогда сравнивать по == совершенно уместно. Либо сравнение с нулём перед возможным делением на это число. В непараметрической статистике для обработки спаренных значений, возможно, адекватнее будет сравнивать по ==, чем вводить какую-то дельту…
Idea, кстати, про возможный NPE в тернарном операторе тоже предупреждает)
Напоминает задачки из java puzzlers. Ещё вот сравнение двух URL вспоминается, когда (new URL(«url1.org»)).equals(new URL(«url2.whatever»)) выдавало true, если у этих url совпадали ip.
Стало интересно, как C# ведёт себя в аналогичных ситуациях.
1.
Ручного боксинга примитивных типов в зоопарк типов нет — проблем нет. (Вообще, странно это. Непонятно, зачем приводить типы.)
2.
Приведения
3.
Какой ProblemFactory замечательный. :) В шарпе формат — это строка. Поглядел документацию — не вкурил, зачем было огород городить, тем более непотокобезопасный. Джависты, можете пояснить, какие это даёт преимущества? А то я в джаве нуб. :)
4.
Здесь шарп в пролёте, потому что
5.
В шарпе такая же хрень с переносами строк, только ни
1.
Number n = flag ? new Integer(1) : new Double(2.0);
Ручного боксинга примитивных типов в зоопарк типов нет — проблем нет. (Вообще, странно это. Непонятно, зачем приводить типы.)
2.
Integer n = flag1 ? 1 : flag2 ? 2 : null;
Приведения
int
к null
нет — проблем нет. (В шарпе нужно писать (int?)null
, чтобы аналог скомпилировался.)3.
private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Какой ProblemFactory замечательный. :) В шарпе формат — это строка. Поглядел документацию — не вкурил, зачем было огород городить, тем более непотокобезопасный. Джависты, можете пояснить, какие это даёт преимущества? А то я в джаве нуб. :)
4.
System.out.println(new BigDecimal(1.1));
Здесь шарп в пролёте, потому что
BigDecimal
нет, есть только BigInteger
. Попробовал с обычным decimal
— шарп не позволил скомпилировать аналогичный код, потому что неявного приведения от double
к decimal
не существует. И даже если явно привести, то эта шибко умная зараза каким-то образом умудряется правильно округлить.5.
System.out.printf("%s\n", "str#1");
В шарпе такая же хрень с переносами строк, только ни
printf
, ни %n
нет. Пишите Environment.NewLine
, чтоб ему пусто было (ну или WriteLine
вместо Write
).Ручной боксинг в жаве нужен также из-за отсутствия возможности использовать примитивные типы в generics, и поэтому, например, когда создается объект HashMap<Integer, String>, можно использовать обычные цифры в качестве ключей, не создавая объекты. Да и для удобства.
На всякий случай, вдруг кто-нибудь ещё не в курсе: TIntObjectHashMap.
Я так же сравнивал C++ и Java в очередном посте про PVS-Studio и получалось, что большинство C++ проблем для Java неактуальны. Потом понял, что в Java не меньше своих проблем, которые в плюсах не возникают. Наверняка и в Шарпе есть специфические проблемы :-)
Про DateFormat подозреваю, что так было сделано с целью улучшить производительность, если один формат используется многократно в рамках одного потока. Тогда строка формата парсится один раз. Конечно то, что сам форматтер не потокобезопасный, это какая-то ужасная кривость. Pattern (прекомпилированное регулярное выражение), например, не менее сложная сущность, но он потокобезопасен. Если затраты производительности вас не смущают, вы всегда можете написать утилитный метод типа
Про DateFormat подозреваю, что так было сделано с целью улучшить производительность, если один формат используется многократно в рамках одного потока. Тогда строка формата парсится один раз. Конечно то, что сам форматтер не потокобезопасный, это какая-то ужасная кривость. Pattern (прекомпилированное регулярное выражение), например, не менее сложная сущность, но он потокобезопасен. Если затраты производительности вас не смущают, вы всегда можете написать утилитный метод типа
public static String formatDate(String format, Date date) {
return new SimpleDateFormat(format).format(date);
}
В целом да, в шарпе есть неочевидные вещи, про которые мало кто (а иногда практически никто) не знает. Чего стоит одно создание делегатов: сколько объектов создастся по лямбдам и method group'ам, при каких условиях — это мозговыносяще (нагромождение обратной совместимости и оптимизаций). Существующие засады ещё и выпиливают (вон, даже создание переменных в цикле for поменяли).
Что меня ужасно радует — в шарпе нет маразмов, которые преследуют тебя на каждом шагу. В джаве есть == у строк и прочие вещи, которые не имеют никакого оправдания кроме перфекционизма. BigDecimal.equals из статьи — из той же серии. В шарпе из таких глобальных обломов могу вспомнить разве что события, принимающие значение null.
Впрочем, в большинстве случаев ReSharper и IDEA помножают такие проблемы на ноль для обоих языков. Причём сразу по мере ввода кода.
Что меня ужасно радует — в шарпе нет маразмов, которые преследуют тебя на каждом шагу. В джаве есть == у строк и прочие вещи, которые не имеют никакого оправдания кроме перфекционизма. BigDecimal.equals из статьи — из той же серии. В шарпе из таких глобальных обломов могу вспомнить разве что события, принимающие значение null.
Впрочем, в большинстве случаев ReSharper и IDEA помножают такие проблемы на ноль для обоих языков. Причём сразу по мере ввода кода.
Работа с датами в Java вообще не лучшим образом сделана. Видимо, тяжелое наследие Java 1.0. Гораздо лучше использовать стороннюю библиотеку типа JodaTime.
{del}
Я как-то несколько лет назад работал в одной конторе, мы занимались переписыванием серверной части сайта symantec.com. И у нас в Эклипсе по их требованию стояли семантековские настройки (правда, мне кажется, это был PMD).
Так вот, там тернарные операторы помечались как ошибка. Т.е. совсем ошибка, код не компилировался, т.е. их нельзя было использовать вообще.
У меня нет мнения, насколько это правильно, просто вспомнил.
Так вот, там тернарные операторы помечались как ошибка. Т.е. совсем ошибка, код не компилировался, т.е. их нельзя было использовать вообще.
У меня нет мнения, насколько это правильно, просто вспомнил.
Отличный инструмент! Спасибо за то, что познакомили с ним!
Маленький вопрос от не-джависта:
Здесь опечатка или
можно написать либоnew BigDecimal("1.1")
, либоBigDecimal.valueOf(1.1)
.
Здесь опечатка или
valueOf(1.1)
как-то специальным образом компилируется, что 1.1
оказывается не IEEE754?Вопрос не ясен. 1.1 не может быть выражено в стандарте IEEE 754 никак. Специально ничего не компилируется.
В посте нет ошибки. Данный метод реализован так:
public static BigDecimal valueOf(double val) {
return new BigDecimal(Double.toString(val));
}
То есть double сперва преобразуется в строку, используя обычный алгоритм, который работает с точностью double, а не с идеальной точностью. А потом уже вызывается конструктор от строки. Поэтому valueOf сработает не так как конструктор от double.То есть ошибка в точности представления числа
Кстати, в посте еще одна опечатка:
— должно быть от string:
1.1
в double
при использовании valueOf(double)
сохранится, поскольку double представляется в IEEE754?Кстати, в посте еще одна опечатка:
А конструктор BigDecimal(double)
напротив работает точно
— должно быть от string:
А конструктор BigDecimal(string)
, напротив, работает точно
То есть ошибка в точности представления числа 1.1 в double при использовании valueOf(double) сохранится, поскольку double представляется в IEEE754?Не понял, что вы имели в виду. Произойдёт следующее:
1. Во время компиляции компилятор заменит 1.1 на ближайшее допустимое число, представимое в IEEE754, а именно на 1.100000000000000088817841970012523233890533447265625.
2. Во время выполнения это число будет преобразовано в строку с использованием метода Double.toString. Этот метод преобразует в строку, имея в виду точность типа double. Когда он поймёт, что получается что-то типа 1.100000000000000, а более точно в типе double всё равно не выразишь, он просто обрежет нули и вернёт результат «1.1».
3. Отработает конструктор BigDecimal от строки «1.1», который создаст объект BigDecimal, представляющий собой число 1.1
А конструктор BigDecimal(double) напротив работает точно
Нет здесь никакой опечатки. Это истинное утверждение и именно то, что я хотел сказать.
Вчера подключил FindBugs у нас на проекте и обнаружил несколько одинаковых ошибок с тернарным оператором.
Есть два метода:
Есть код, который вызывает один из этих методов:
, где s — типа String.
И суть ошибки — вызывается метод с примитивом (int) и если s == null, то будет вызван m(((Integer)null).intValue()); что приведёт к NPE.
Если не кастить null к Integer, или вместо Integer.parseInt(s) использовать Integer.valueOf(s) то будет вызываться метод m(Integer). Но из за стечения обстоятельств, говнокода и кривых рук будет вызываться метод m(int) и в случае s == null будет NPE
Есть два метода:
static void m(int i) {}
static void m(Integer i) {}
Есть код, который вызывает один из этих методов:
m(s != null ? Integer.parseInt(s) : (Integer) null);
, где s — типа String.
И суть ошибки — вызывается метод с примитивом (int) и если s == null, то будет вызван m(((Integer)null).intValue()); что приведёт к NPE.
Если не кастить null к Integer, или вместо Integer.parseInt(s) использовать Integer.valueOf(s) то будет вызываться метод m(Integer). Но из за стечения обстоятельств
FindBugs это хорошо. Но предлагаю попробовать PVS-Studio для Java :).
Sign up to leave a comment.
FindBugs помогает узнать Java лучше