Effective Java: General Programming (1/2)

Эта глава книги Effective Java собрала более общие советы по написанию кода на Java (и не только). Параграфов довольно много, поэтому я разбил свой сжатый пересказ на две части.

Item 45. Минимизируйте область видимости локальных переменных.
Это улучшает читабельность кода и уменьшает число потенциальных ошибок.
Прежде всего, по возможности объявлять локальную переменную надо там, где она впервые используется. Далее, почти все объявления локальных переменных должны содержать инициализацию. Исключение - когда инициализацию нужно поместить в try/catch блок, а сама переменная нужна снаружи него.
Отдельно следует отметить циклы. Цикл for предоставляет возможность объявлять переменные цикла (loop variables) доступными только для тела самого цикла, что делает его более предпочтительным, нежели циклы while.


Item 46. Предпочитайте for-each циклы вместо for.
Опять же, это делает код более читабельным и уменьшает шансы ошибиться по невнимательности. Но есть и случаи, когда for-each не подходит: фильтрация (когда нужно удалять элементы из коллекции, нужен явный итератор), трансформация (когда нужно заменять одни элементы списка или массива на другие, нужен явный итератор или переменная-индекс), параллельная итерация по нескольким коллекциям.

Item 47. Знайте и применяйте библиотеки.
Изобретать колёса не имеет смысл, потому что:
  • это лишняя трата времени;
  • вряд ли получится лучше, чем уже написано ранее;
  • библиотеки совершенствуются без вашего участия.
Полезно иногда смотреть в release notes новых версий библиотек.
Приведён хороший пример кривого колеса: самописная функция, возвращающая псевдослучайное число из интервала [0, n):
static int random(int n) {
    return Math.abs(rnd.nextInt()) % n;
}
Функция выглядит нормально, но в ней скрыты целых три изъяна. Предлагается попробовать поискать их самостоятельно, однако два их них весьма нетривиальны (я-то их сам точно не нашёл бы). Эти недостатки перечислены в конце поста.
Вместо такой функции следует использовать Random.nextInt(int), где этих проблем нет.

Item 48. Избегайте float и double, если нужны точные результаты.
Типы float и double представляют собой числа с плавающей запятой (floating point). Это означает, что они хранят не точное значение числа, а максимально близкое к нему приближение. Например, они не могут точно хранить числа 0.1, 0.01, 0.001 и т.д. Если задача требует точных результатов, лучше использовать BigDecimal (хоть они и менее удобны и быстры, чем примитивы), либо сводить эту задачу к целым числам (например, считать деньги не в рублях, а копейках).

Item 49. Предпочитайте примитивы вместо объектных типов.
(здесь и далее под объектным типом подразумевается объектная обёртка примитива (boxed primitive))
Автобоксинг и автоанбоксинг (Java 1.5) размыл, но не стёр различия между ними. А различия таковы:
  • у объектных типов могут существовать разные экземпляры с одинаковыми значениями, что не позволяет сравнивать их между собой с помощью ==, как примитивы;
  • объектные типы допускают дополнительное нефункциональное значение null;
  • примитивы экономичнее в плане производительности и использования памяти.
Когда следует применять объектные типы:
  • при работе с коллекциями и другими параметризированными классами;
  • при рефлексивном (reflective) вызове методов.
В остальных случаях, когда есть выбор, рекомендуется применять примитивы. Если приходится иметь дело с объектными типами, следует помнить:
  • если программа сравнивает объектные типы с помощью ==, то сравниваются ссылки на объекты, что почти всегда не то, что вы хотите;
  • когда в выражении замешаны и примитивы, и объектные типы, выполняется unboxing последних;
  • unboxing может вызвать NullPointerException.

Item 50. Избегайте строк, если другие типы больше подходят.
Строки - не замена для других типов значений. Поступающая в программу информация, будь то чтение из файла, обмен по сети или ввод с клавиатуры, чаще всего приходит именно в текстовом виде и должна храниться в String только если действительно представляет собой некоторый текст. Если же есть более подходящий тип для данных (числовые типы, boolean, ...), нужно использовать его.
Строки - не замена для enum-констант. Об этом было в Item 30.
Строки - не замена для составных типов. Так делать плохо:
String compoundKey = className + "#" + i.next();
Для составных типов лучше писать составные классы.
Строки не годятся для предоставления доступа к чему-либо. Имеется в виду то, что, например, строки нехорошо использовать в качестве ключей доступа к определённым данным в том случае, если клиентский код может нарушить целостность этих данных, предоставив неправильный или «чужой» ключ.



Далее спойлер (см. Item 47)

.

.

.

.

.

.

.

Три изъяна функции из Item 47:
  • Если rnd.nextInt() выдаст Integer.MIN_VALUE, то функция вернёт отрицательное значение.
  • Если n - малая степень двойки, то последовательность генерируемых чисел довольное скоро начнёт повторять саму себя.
  • Если n - не степень двойки, то некоторые псевдослучайные числа будут генерироваться чаще других.

Комментариев нет:

Отправить комментарий