- Как общаться с null в Java и не страдать
- Авторизуйтесь
- Как общаться с null в Java и не страдать
- Что такое null в Java
- Защита от null, ключевое слово let
- Элвис-оператор ?:
- Оператор !!
- Ключевое слово let
- Support Annotations — аннотации для кода
- Аннотации для ресурсов
- @NONNULL/@NULLABLE
- @CheckResult — ожидание результата
- Аннотации для потоков
- Аннотация для цвета
- Диапазон значений
- Аннотации для разрешений
- Суперкласс
- Остальные аннотации
Как общаться с null в Java и не страдать
Авторизуйтесь
Как общаться с null в Java и не страдать
Java и null неразрывно связаны. Трудно найти Java-программиста, который не сталкивался с NullPointerException . Если даже автор понятия нулевого указателя признал его «ошибкой на миллиард долларов», почему он сохранился в Java? null присутствует в Java уже давно, и я уверен, что разработчики языка знают, что он создает больше проблем, чем решает. Это удивительно, ведь философия Java — делать вещи как можно более простыми. Если разработчики отказались от указателей, перегрузки операторов и множественного наследования, то почему они оставили null ? Я не знаю ответа на этот вопрос. Однако не имеет значения, насколько много критики идет в адрес null в Java, нам придется с этим смириться. Вместо того, чтобы жаловаться, давайте лучше научимся правильно его использовать. Если быть недостаточно внимательным при использовании null , Java заставит вас страдать с помощью ужасного java.lang.NullPointerException . Наиболее частая причина NullPointerException — недостаточное понимание тонкостей использования null . Давайте вспомним самые важные вещи о нем в Java.
Что такое null в Java
Как мы уже выяснили, null очень важен в Java. Изначально он служил, чтобы обозначить отсутствие чего-либо, например, пользователя, ресурса и т. п. Но уже через год выяснилось, что он приносит много проблем. В этой статье мы рассмотрим основные вещи, которые следует знать о нулевом указателе в Java, чтобы свести к минимуму проверки на null и избежать неприятных NullPointerException .
1. В первую очередь, null — это ключевое слово в Java, как public , static или final . Оно регистрозависимо, поэтому вы не сможете написать Null или NULL , компилятор этого не поймет и выдаст ошибку:
Эта проблема часто возникает у программистов, которые переходят на Java с других языков, но с современными средами разработки это несущественно. Такие IDE, как Eclipse или Netbeans, исправляют эти ошибки, пока вы набираете код. Но во времена Блокнота, Vim или Emacs это было серьезной проблемой, которая отнимала много времени.
2. Так же, как и любой примитивный тип имеет значение по умолчанию (0 у int , false у boolean ), null — значение по умолчанию любого ссылочного типа, а значит, и для любого объекта. Если вы объявляете булеву переменную, ей присваивается значение false . Если вы объявляете ссылочную переменную, ей присваивается значение null , вне зависимости от области видимости и модификаторов доступа. Единственное, компилятор предупредит о попытке использовать неинициализированную локальную переменную. Для того, чтобы убедиться в этом, вы можете создать ссылочную переменную, не инициализируя ее, и вывести ее на экран:
Это справедливо как для статических, так и для нестатических переменных. В данном случае мы объявили myObj как статическую переменную для того, чтобы ее можно было использовать в статическом методе main .
4–5 декабря, Онлайн, Беcплатно
3. Несмотря на распространенное мнение, null не является ни объектом, ни типом. Это просто специальное значение, которое может быть присвоено любому ссылочному типу. Кроме того, вы также можете привести null к любому ссылочному типу:
Как видите, приведение null к ссылочному типу не вызывает ошибки ни при компиляции, ни при запуске. Также при запуске не будет NullPointerException , несмотря на распространенное заблуждение.
4. null может быть присвоен только переменной ссылочного типа. Примитивным типам — int , double , float или boolean — значение null присвоить нельзя. Компилятор не допустит этого и выдаст ошибку:
Итак, попытка присвоения значения null примитивному типу — ошибка времени компиляции, но вы можете присвоить null типу-обертке, а затем присвоить это значение соответствуему примитиву. Компилятор ругаться не будет, но при выполнении кода будет брошено NullPointerException . Это происходит из-за автоматического заворачивания (autoboxing) в Java
5. Любой объект класса-обертки со значением null кинет NullPointerException при разворачивании (unboxing). Некоторые программисты думают, что обертка автоматически присвоит примитиву значение по умолчанию (0 для int , false для boolean и т. д.), но это не так:
Если вы запустите этот код, вы увидите Exception in thread «main» java.lang.NullPointerException в консоли. Это часто случается при работе с HashMap с ключами типа Integer . Код ниже сломается, как только вы его запустите:
Этот код выглядит простым и понятным. Мы ищем, сколько каждое число встречается в массиве, это классический способ поиска дубликатов в массиве в Java. Мы берем предыдущее значение количества, инкрементируем его и кладем обратно в HashMap . Мы полагаем, что Integer позаботится о том, чтобы вернуть значение по умолчанию для int , однако если числа нет в HashMap , метод get() вернет null , а не 0. И при оборачивании выбросит NullPoinerException . Представьте, что этот код завернут в условие и недостаточно протестирован. Как только вы его запустите на продакшен – УПС!
6. Оператор instanceof вернет false , будучи примененным к переменной со значением null или к литералу null :
Это важное свойство оператора instanceof , которое делает его полезным при приведении типов.
7. Возможно, вы уже знаете, что если вызвать нестатический метод по ссылке со значением null , результатом будет NullPointerException . Но зато вы можете вызвать по ней статический метод класса:
Результат выполнения этого кода:
8. Вы можете передавать null в любой метод, который принимает ссылочный тип, например, public void print(Object obj) может быть вызван так: print(null) . С точки зрения компилятора ошибки здесь нет, но поведение такого кода целиком зависит от реализации метода. Безопасный метод не кидает NullPointerException в этом случае, а тихо завершает работу. Если бизнес-логика позволяет, лучше писать безопасные методы.
9. Вы можете сравнивать null , используя оператор == («равно») и != («не равно»), но не с арифметическими или логическими операторами (такими как «больше» или «меньше»). В отличие от SQL, в Java null == null вернет true :
Вывод этого кода:
Вот и все, что надо знать о null в Java. При наличии небольшого опыта и с помощью простых приемов вы можете сделать свой код безопасным. Поскольку null может рассматриваться как пустая или неинициализированная переменная, важно документировать поведение метода при получении null . Помните, что любая созданная и не проинициализированная переменная имеет по умолчанию значение null и что вы не можете вызвать метод объекта или обратиться к его полю, используя null .
Источник
Защита от null, ключевое слово let
В Java существует проблема с исключением NullPointerException и разработчику нужно постоянно устраивать проверки на null. В Kotlin существует защита от подобных ошибок. Нужно использовать вопросительный знак ? у типа.
Любой объект может быть null и мы можем явно указать это через символ вопроса ?. В этом случае Kotlin будет проверять на возможность ошибки и предупреждать на этапе разработки.
Любой тип, не поддерживающий null, является дочерним типом соответствующего типа, поддерживающего null, поэтому не путайте следующие выражения:
Умное приведение Smart cast позволяет превратить переменную из одного состояния в другое: cat? превратится в cat (см. пример выше) или переменная a типа Int? превратится в Int.
Если умное приведение вам не нужно, то используйте запись с безопасным оператором ?.
Функция будет вызвана только в том случае, если значение a отлично от null. Безопасные вызовы можно сцеплять.
Не путайте null с пустой строкой «». Выводим значение переменной типа String.
Любую функцию или параметр конструктора можно объявить с null-совместимым типом. Следующий код определяет функцию printInt(), которая получает параметр типа Int? (null-совместимый Int):
Функция может иметь null-совместимый возвращаемый тип.
Можно создать массив нулевых объектов. При этом массив может содержать и строки и null.
Элвис-оператор ?:
Другой «Элвис-оператор» ?: (напоминает причёску Элвиса Пресли, если повернуть голову, как на смайликах) позволяет назначить альтернативное значение, если объект равен null.
Справа от элвис-оператора можно использовать return и выбрасывать исключения.
Оператор !!
Если вы точно уверены, что ваша переменная не null, то можете использовать оператор !!. Kotlin будет полагаться на ваш профессионализм и не станет проверять ваше предположение. Если вы ошиблись в своём предположении, то ваше приложение может грохнуться.
Например, в Android часто объявляются компоненты, а инициализация происходит позже.
Подобный способ позволяет избежать null и аналогичен записи в Java.
При полной уверенности можете написать
Ключевое слово let
Ещё один способ избежать проблем с null — это использовать ключевое слово let вместе с оператором ?.:
Код будет выводить имя кота только в том случае, если объект не равен null.
Этот подход удобен, когда имеются смешанные варианты и мы хотим выполнения кода только для объектов, которые не имеют значения null:
В массиве три элемента, но на экран выводятся только два элемента.
Такой подход сокращает и упрощает код. Допустим, у нас есть функция getBestCat(), возвращающий тип Cat?.
Можем вызвать с проверкой на null.
А можно обойтись без создания новой переменной, а сразу использовать let:
Запись означает следующее: получить объект «Самый лучший кот», и если объект не null, то позволить ему есть (eat()).
Источник
Support Annotations — аннотации для кода
Начиная с версии 19.1 библиотека Android support library включает себя аннотации, которые помогают улучшить код, уменьшая количество ошибок. Кстати, сама библиотека и многие другие классы системы уже используют новые аннотации в своём коде и вы их можете иногда видеть при наборе своего текста. Количество аннотаций увеличивается, поэтому сверяйтесь с документацией.
По умолчанию аннотации не включены, они идут как отдельная библиотека. Но она входит в состав библиотеки appcompat. Если вы не используете appcompat, то подключите библиотеку аннотаций самостоятельно.
Так как на сегодняшний день проекты по умолчанию используют appcompat, то будем считать, что аннотации всегда готовы к употреблению.
Подсказки с аннотацией появляются при попытке ввести неверный тип параметра в методе, который предусмотрительно снабжён аннотацией.
Аннотации для ресурсов
Для первого знакомства приведу пример с использованием ресурсов. В коде мы часто используем ресурсы строк, изображений, идентификаторов и т.п. По сути ресурс в данном случае является числом типа int. Подготовим ресурсы для имён котов в res/strings.xml
Напишем вспомогательный метод для генерации имени кота, используя строковый ресурс.
Студия не замечает ошибок, метод написан правильно и даже работает.
Но никто вам не помешает написать и такие варианты:
Два варианта используют корректные типы, но результат будет не тот, который мы хотели увидеть. В первом случае выведется путь к ресурсу (к счастью, программа продолжит работу), а во втором произойдёт ужасное — программа пойдёт к коту под хвост.
Как избежать этих ошибок, перепишем метод с добавлением аннотации:
Теперь студия будет подчёркивать неправильные параметры 451 или R.mipmap.ic_launcher и выводить предупреждение, что она ожидает строковые ресурсы, а не любые выдуманные вами числа.
Вы можете использовать аннотации @AnimatorRes, @AnimRes, @AnyRes, @ArrayRes, @AttrRes, @BoolRes, @ColorRes, @DimenRes, @DrawableRes, @FractionRes, @IdRes, @IntegerRes, @InterpolatorRes, @LayoutRes, @MenuRes, @PluralsRes, @RawRes, @StringRes, @StyleableRes, @StyleRes, @XmlRes и т.д. Если у вас есть свой тип ресурсов «foo», то можете использовать аннотацию FooRes.
@NONNULL/@NULLABLE
Поняв базовый принцип, вы теперь можете использовать и другие аннотации. Например, указать, что параметр не может быть null или, наоборот, может принимать значение null.
Студия выводит предупреждение, но позволит запустить программу. Нажатие на кнопку и опять ваш труд коту под хвост — крах приложения.
@CheckResult — ожидание результата
Метод может возвращать какое-то значение, которое нужно применить. А можно просто вызвать метод, только какой в этом смысл, если возвращаемое значение нигде не используется? Для тех, кто страдает склерозом — встречайте аннотацию @CheckResult.
Студия укажет на ошибку и откажется запускать ваше приложение. Умница.
Аннотация для продвинутых. ProGuard удаляет неиспользуемые методы при компиляции. Если она делает это ошибочно или по каким-то другим причинам вам нужно оставить метод в приложении, то используйте @Keep:
Аннотации для потоков
Существуют специальные аннотации для указания потоков.
Например, мы знаем, что методы класса AsyncTask могут работать только в определённых потоках.
Если в методе doInBackground() обратиться к какому-нибудь компоненту, то студия предупредит, что так делать нельзя.
@UiThread и @MainThread практически совпадают, но в редких случаях могут различаться. Подробности в документации.
Аннотация для цвета
Вы можете задать цвет через ресурс, используя аннотацию @ColorRes. А если перед вами стоит противоположная задача — указать значение цвета через RGB/ARGB, то используйте аннотацию @ColorInt. В этом случае при использовании цветового ресурса студия покажет ошибку.
Диапазон значений
Можно указать диапазон значений для типов float/double через аннотацию @FloatRange:
В этом случае можно использовать значения от 0.0 до 1.0. Любая попытка ввести другое значение приведёт к предупреждению.
Аналогично работает для типов int/long через аннотацию @IntRange:
Для массивов, коллекций и строк можно использовать аннотацию @Size для установки ограничений в размерах или длине строк.
- Если коллекция не должна быть пустой — @Size(min=1)
- Максимальная длина строки 15 символов — @Size(max=15)
- Массив точно состоит из двух элементов — @Size(2)
- Длина массива должна быть кратна двум (например, координаты точек X и Y) — @Size(multiple=2)
Аннотации для разрешений
Если ваш метод использует функциональность, которая доступна через разрешения, то можете указать через аннотацию @RequiresPermission:
Если имеются несколько подходящих разрешений, то можно использовать атрибут anyOf, чтобы выбрать один из них:
А если нужно выбрать несколько разрешений, то используйте атрибут allOf:
Разрешения для намерений
Если есть разрешения на чтение или запись, то можно указать нужный режим через аннотации @Read или @Write:
Суперкласс
Если метод должен использовать вызов суперкласса, то используем аннотацию @CallSuper:
Остальные аннотации
В документации вы можете узнать о других аннотациях: IntDef/StringDef, @VisibleForTesting и т.д.
Источник