Что такое var java
Ключевое слово var в Java: что, зачем и почему
Разбираемся, что за var такой и в каких ситуациях он может пригодиться.
robby mccullough / unsplash
Что случилось?
Начиная с версии 10, в Java появилось ключевое слово var. Новая фича — local variable type inference (выведение типа локальной переменной) — не даёт переменным дополнительных возможностей. Впрочем, и ограничений на них не накладывает. Просто разработчикам не нужно теперь писать лишний код при объявлении переменных, когда их тип очевиден из контекста.
В каких случаях тип переменной очевиден?
Если переменной сразу же присваивается значение, для которого компилятор может однозначно понять тип. Вот три типичных ситуации, когда удобно перейти от явного указания типа к var:
1. При создании нового экземпляра класса. Особенно если у этого класса длинное название.
В этом случае компилятор «догадывается», что у переменной theLongestNameYouCanEverImagine должен быть тип TheLongestNameYouCanEverImagine.
2. В заголовке цикла.
Здесь переменной i неявно устанавливается тип int.
Если инициализировать переменную целым числом, то по умолчанию для неё будет определён тип int. Чтобы компилятор решил иначе, нужны подсказки-постфиксы: L — для типа long, F — для float, D — для double, или явное приведение к другому типу.
3. В блоке try-with-resources.
Тут в заголовке блока инициализируются две локальные переменные: у reader будет тип BufferedReader, у writer — BufferedWriter.
Присвоить значение сразу же означает, что нельзя сначала просто дать var-переменной имя и только следующим оператором инициализировать её:
А ещё важно не перепутать окончание оператора с концом строки. Операторы в Java не прерываются переносами строк, поэтому разрешается объявлять переменную в нескольких строках:
Первый контакт с «var» в Java 10
Java 10 будет выпущен 20 марта 2018 года, и все фичи, которые должны быть в этом релизе, уже объединены в основную ветку разработки. Одним из самых интересных нововведений Java 10 безусловно является вывод типа локальной переменной (JEP 286). Это дает вам возможность сократить объявления переменных используя новое ключевое слово var:
И это все, спасибо за внимание!
Нет, я уверен, что вам интересно узнать больше. Под катом я расскажу, где применяется var, а где нет, как это влияет на читаемость кода и что произошло с val.
Замена объявлений типа с помощью var
В качестве разработчика Java мы привыкли дважды вводить типы, один раз для объявления переменной и второй раз для следующего за ней конструктора:
Мы также часто объявляем типы переменных, которые используются только один раз:
Это не особенно страшно, но всё же несколько избыточно. И хотя IDE могут помочь в написании такого кода, читаемость страдает, когда имена переменных перескакивают вправо-влево, потому что названия типов имеют разную длину или когда разработчики избегают объявления промежуточных переменных, потому что объявления типов будут отвлекать на себя много внимания, не принося никакой пользы.
Начиная с Java 10 у разработчиков появится альтернатива — они могут позволить компилятору вывести тип с помощью var:
При обработке var, компилятор просматривает правую часть объявления, так называемый инициализатор и использует его тип для переменной. И это нужно не только для внутренних расчётов, полученный тип будет также записан в итоговый байт-код.
Как вы видите, это экономит несколько символов при наборе текста, но, что более важно, он дедуплицирует избыточную информацию и аккуратно выравнивает имена переменных, что облегчает их чтение. Стоимость очевидна: некоторые типы переменных, например connection, не сразу очевидны. IDE могут, конечно же, показывать их по требованию, но это не помогает ни в какой другой среде (например, при просмотре кода в браузере — на stackoverflow или даже в этой статье — прим. перев.).
Кстати, если вы беспокоитесь о конфликтах с методами и переменными с именем var: не нужно. Технически, var — это не ключевое слово, а зарезервированное имя типа, то есть его можно использовать только в тех местах, где компилятор ожидает имя типа, но во всех остальных местах он является допустимым идентификатором. Это означает, что только классы, называемые var, больше не будут работать, но это не особо частый случай.
Вывод типа локальной переменной выглядит как простая функция, но это обманчиво. Возможно, у вас уже есть некоторые вопросы:
Нет, это не JavaScript
Я хочу начать с подчеркивания того, что var не изменяет приверженность Java статической типизации ни на йоту. Компилятор отображает все задействованные типы и помещает их в файлы классов, как если бы вы их вводили сами.
Например, вот результат декомпиляции IntelliJ (фактически Fernflower) файла класса с примером URL:
Это байт в байт тот же результат, как если бы я сам объявил типы. Фактически вывод типов существует только во время компиляции и никак не влияет на итоговый байт-код, что также означает отсутствие какого-либо влияния на производительность. Так что расслабьтесь, это не Javascript, и никто не собирается превращать 0 в бога.
Если вы все еще обеспокоены тем, что отсутствие явных типов делает код хуже, у меня есть вопрос для вас: вы когда-нибудь писали лямбда-выражения, не определяя типы аргументов?
Где использовать var (и где не нужно)
Название JEP 286, «вывод типа локальной переменной», немного намекает на то, где var можно использовать: для локальных переменных. Точнее, для «локальных объявлений переменных с инициализаторами», так что следующий код работать не будет:
Код должен быть таким: var foo = »Foo». Даже тогда это распространяется не на все случаи, так как var не будет работать с так называемыми «poly expressions», такими как лямбда-выражения и ссылки на методы, тип которых компилятор определяет в отношении ожидаемого типа:
Единственное подходящее место, помимо локальных переменных — это цикл for:
Это означает, что поля, сигнатуры методов и выражения catch все еще требуют ручного объявления типа.
Устранение ошибок «Действие на расстоянии»
То, что var может использоваться только локально, является не техническим ограничением, а конструктивным решением. Конечно, было бы неплохо, если бы он работал так:
Компилятор мог бы легко просмотреть все присвоения и вывести наиболее конкретный тип, который подходит для каждого из них, но он не делает этого. Команда JDK хотела избежать ошибок «действия на расстоянии», что означает, что изменение кода в некотором месте не должно приводить к, казалось бы, несвязанной ошибке далеко в другой части системы.
В качестве примера рассмотрим следующее:
Пока что всё идёт… Я не хочу говорить «хорошо», но вы знаете, что я имею в виду. Я уверен, что вы видели такой код. Теперь добавим эту строку:
Что произойдет? Это не риторический вопрос, подумайте об этом.
Ответ заключается в том, что if-условие вызывает ошибку, потому что id больше не будет int и поэтому не может сравниться с 100. Эта ошибка находится на довольно большом расстоянии от изменения, вызвавшего ее. Кроме того, это непредвиденное следствие простого присваивания значения переменной.
С этой точки зрения, решение ограничить вывод типа до непосредственного объявления имеет смысл.
Почему не могут быть выведены типы полей и методов?
Поля и методы имеют гораздо большую область видимости, чем локальные переменные, и поэтому расстояние между изменениями и ошибками значительно возрастает. В худшем случае изменение типа параметра метода может привести к двоичной несовместимости и, следовательно, ошибкам времени выполнения. Это довольно экстремальное следствие изменения некоторых деталей реализации.
Так как не приватные поля и методы становятся частью контракта класса, и поскольку они не должны просто так меняться, их типы не выводятся. Конечно, исключение можно было бы сделать для приватных полей или методов, но это сделало бы вывод типов довольно запутанным.
Основная идея заключается в том, что локальные переменные являются деталями реализации и не могут ссылаться на «далекий» код, что уменьшает необходимость строгого, явного и подробного определения их типа.
Предпосылки появления var
Давайте посмотрим за кулисы и узнаем, почему был введен var, как он должен повлиять на читаемость и почему нет val (или let), сопровождающего его. Если вас интересует наиболее подробная информация, посмотрите дискуссии JEP 286, часто задаваемые вопросы и список рассылки Project Amber.
Но зачем?!
Java склонна быть довольно многословной, особенно по сравнению с более молодыми языками, это является одним из слабых мест языка и общей темой для критики новичками и опытными разработчиками Java. Project Amber, в рамках которого был разработан var, направлен на «изучение и инкубацию небольших, ориентированных на продуктивность разработки функций Java-языка», и цель состоит в том, чтобы в целом сократить рутину, связанную с написанием и чтением кода на Java.
Вывод типа локальной переменной согласуется с этой целью. С точки зрения написания кода, это явно упрощает объявление переменных, хотя я бы предположил, что добрая половина моих объявлений генерируется средой IDE, либо во время рефакторинга, либо потому что проще написать вызов конструктора или метода, а затем создать для него переменную.
Помимо упрощения объявлений это также делает их более податливыми. Что я имею в виду? Объявления могут быть довольно уродливыми, особенно если речь идет о названиях обобщённых классов в корпоративных приложениях.
Это чертовски длинное имя типа, которое выталкивает имя переменной в конец и оставляет вас либо с растянутой до 150 символов строкой, либо инициализацией переменной в новой строке. Оба варианта отстой, если вы нацелены на удобочитаемость.
С var это гораздо менее обременительное и простое для глаз объявление промежуточных переменных, и мы могли бы сделать это в тех местах, где раньше не делали. Подумайте о вложенных или последовательных выражениях, которые вы решили не разбивать, потому что уменьшение их сложности компенсировалось увеличением числа рутинных действий. Разумное использование var может сделать промежуточные результаты более очевидными и более доступными.
Короче говоря, var — это про сокращение многословия и рутины, а не об экономии символов.
А что относительно читаемости?
Теперь перейдем к читаемости. Несомненно, когда типы отсутствуют, читаемость должна ухудшиться, не так ли? Вообще говоря, да. Когда вы пытаетесь понять, как работает часть кода, типы являются важным компонентом. И даже если бы IDE разработали функции, позволяющие отображать все выведенные типы, это все равно было бы более косвенным, чем если бы они всегда присутствовали в исходнике.
Таким образом, var сразу приносит нам недостаток читаемости и должен компенсировать это. Один из способов — выравнивание имен переменных:
Имена типов важны, но имена переменных могут быть важнее. Типы описывают общую концепцию в контексте всей экосистемы Java (для классов JDK), общий вариант использования (библиотека или фреймворк) или бизнес-домен (приложение) и, следовательно, всегда будут иметь общие имена. Переменные, с другой стороны, определены в конкретном и очень малом контексте, в котором их имя может быть очень точным.
С var имена переменных выходят на первый план и выделяются так, как раньше этого не делали, особенно если подсветка кода отмечает ключевое слово и, таким образом, позволяет инстинктивно игнорировать его. Я какое-то время проводил час или два в день, читая Kotlin, и я тут же привык к этому. Это может значительно улучшить читаемость.
Как говорилось выше, другое улучшение читаемости может происходить из-за того, что объявлено больше промежуточных переменных, поскольку это связано со снижением издержек при записи и чтении.
Поиск стиля
Разумеется, легко переборщить с var и получить код с бестолковыми именами переменных и без видимых типов. Мы должны, сообщество в целом и каждая команда в частности, придумать стиль, который соответствует нашим потребностям и балансирует между многословием и ясностью.
Брайан Гетц (Brian Goetz), архитектор языка Java в Oracle и ответственный за Project Amber, дал первую эвристику:
Используйте конструкцию var, когда она делает код более понятным и более кратким, и вы не теряете существенную информацию.
В связи с этим я надеюсь, что IDE не будут вообще предупреждать нас, если объявление типа может быть заменено на var. Это не универсальная конструкция, как лямбда-выражения.
Почему нет val/let?
Многие языки с var также предлагают дополнительное ключевое слово для неизменяемых переменных. Обычно это называется val, иногда let, но Java 10 не имеет ни того, ни другого, и вместо этого мы должны использовать final var. Вот несколько причин:
Чтож, возможно в будущем… До тех пор мы должны использовать final var.
Подводя итоги
При объявлении локальных переменных вы можете использовать var вместо имени класса или интерфейса, чтобы позволить компилятору вывести тип. Это работает только в том случае, если переменная немедленно инициализируется, например, как в var s = «». Индексы для циклов также могут быть объявлены с помощью var. Тип, выводимый компилятором, помещается в байт-код, поэтому во время выполнения ничего не меняется — Java все еще является статически типизированным языком.
Помимо локальных переменных, например в полях и сигнатурах методов, var не может применяться. Это было сделано, чтобы избежать ошибок «действия на расстоянии» и сохранить место использования выведенной переменной рядом с местом объявления, что смягчает опасения, связанные с читабельностью.
Хотя бездумное использование var может сделать код хуже, тем не менее это шанс для Java-разработчиков написать более читабельный код, найдя новый баланс между шумом объявлений и сложностью вложенных / последовательных выражений.
26 рекомендаций по использованию типа var в Java
The Java Local Variable Type Inference (LVTI) или кратко — тип var (идентификатор var — это не ключевое слово, а зарезервированное имя типа) был добавлен в Java 10 с помощью JEP 286: Local-Variable Type Inference. Являясь 100% функцией компилятора, она не влияет на байт-код, время выполнения или производительность. В основном компилятор проверяет правую часть от оператора присваивания и, исходя из нее, определяет конкретный тип переменной, а затем заменяет им var.
Появление var не означает, что его везде и всегда удобно использовать, иногда более практично будет обойтись стандартными средствами.
В этой статье мы рассмотрим 26 ситуаций, с примерами того, когда можно использовать var, а когда этого делать не стоит.
Пункт 1: старайтесь давать осмысленные имена локальным переменным
Обычно мы фокусируемся на том, чтобы давать правильные имена полям классов, но мы не уделяем такого же внимания именам локальных переменных. Когда наши методы отлично реализованы, содержат мало кода и имеют хорошие имена, то очень часто мы не обращаем внимание на локальные переменные, а то и вовсе сокращаем их имена.
Когда мы используем var вместо написания явных типов, то компилятор определяет их автоматически и подставляет вместо var. Но с другой стороны в результате этого людям становится труднее читать и понимать код, так как использование var может усложнить его читаемость и понимание. В большинстве случаев это происходит потому, что мы склонны смотреть на тип переменной, как на первичную информацию, а на ее имя, как на вторичную. Хотя должно быть как раз наоборот.
Пример 1:
Наверно, многие согласятся, что в примере ниже имена локальных переменных слишком короткие:
При использовании коротких имен, совместно с var, код становится еще менее понятным:
Более предпочтительный вариант:
Пример 2:
Избегайте подобного именования переменных:
Используйте более осмысленные имена:
Пример 3:
В стремлении давать более понятные имена локальным переменным, не впадайте в крайности:
Вместо этого можно использовать более краткий, но не менее понятный вариант:
Знаете ли вы, что у Java есть внутренний класс с именем:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
Что же, именование переменных с таким типом может быть непростым делом 🙂
Пункт 2: используйте литералы чтобы помочь var точно определить тип примитива (int, long, float, double)
Без использования литералов для примитивных типов мы можем обнаружить, что ожидаемые и предполагаемые типы могут различаться. Это вызвано неявным приведением типов, который используется var-переменными.
Например, следующие два фрагмента кода ведут себя, как и ожидалось. Тут мы явно объявляем типы boolean и char:
Теперь используем var, вместо явного объявления типов:
Пока все хорошо. А теперь сделаем то же самое для типов int, long, float и double:
Хотя приведенный выше фрагмент кода прост и понятен, теперь давайте воспользуемся var, вместо явного указания типов.
Все четыре переменные будут выведены, как int. Чтобы исправить это поведение, нам нужно использовать литералы Java:
Но что случится, если мы объявим число с десятичной частью?
Избегайте этого, если ожидаете получить переменную типа float:
Что бы избежать неожиданности, используйте соответствующий литерал:
Пункт 3: в некоторых случаях var и неявные приведения типов могут упростить поддержку кода
Например, давайте предположим, что наш код находится между двумя методами. Один метод получает корзину покупок с разными товарами и вычисляет лучшую цену. Для этого он сравнивает различные цены на рынке и возвращает общую цену в виде типа float. Другой метод просто списывает эту цену с карты.
Во-первых, давайте посмотрим на метод, который вычисляет лучшую цену:
Во-вторых, давайте взглянем на метод, который работает с картой:
Теперь мы помещаем наш код между этими двумя внешними сервисными методами в качестве клиента. Наши пользователи могут выбирать товары для покупки, и мы рассчитываем лучшую цену для них, а затем списываем средства с карты:
Через некоторое время компания, которая владеет API, решает отказаться от вещественного представления цен в пользу десятичного (вместо float теперь используется int). Итак, они модифицировали код API следующим образом:
Дело в том, что наш код использует явное объявление float переменной в качестве цены. В его нынешнем виде мы будем получать ошибку во время компиляции. Но если бы мы предвидели такую ситуацию и использовали var вместо float, то наш код продолжил бы работать без проблем, благодаря неявному приведению типов:
Пункт 4: когда литералы не являются подходящим решением, то используйте явное приведение типов или откажитесь от var
Некоторые примитивные типы в Java не имеют специальных литералов, например, типы byte и short. В этом случае, используя явное обозначение типов, мы можем создавать переменные без каких-либо проблем.
Используйте это вместо var:
Но зачем в данной ситуации отдавать предпочтение явному обозначению типов вместо того, чтобы просто использовать var? Что же, давайте напишем этот код, используя var. Обратите внимание, что в обоих случаях компилятор предположит, что вам нужны переменные типа int.
Избегайте этой ошибки:
Здесь нет литералов, которые пришли бы нам на помощь, потому мы вынуждены использовать явное нисходящее приведение типов. Лично я буду избегать таких ситуаций, поскольку не вижу здесь никаких преимуществ.
Прибегайте к подобной записи только, если вы действительно хотите использовать var:
Пункт 5: избегайте использования var, если названия переменных не содержат достаточной информации о типе для понимания кода
Преимущество использования var заключается в написании более лаконичного кода. Например, в случае использования конструкторов, мы можем избежать необходимости повторения имени класса и, следовательно, устранить избыточность кода.
Вместо этого используйте:
Для конструкции, приведенной ниже, var также станет удачным способом упрощения кода без потери информативности.
Используйте следующий код:
Итак, почему нам более комфортно работать с var в представленных примерах? Потому, что вся необходимая информация содержится в названиях переменных. Но если var, в сочетании с именем переменной, приводит к уменьшению ясности кода, лучше откажитесь от его использования.
Пункт 6: тип var гарантирует безопасность во время компиляции
Это означает, что мы не можем скомпилировать приложение, которое попытается выполнить неправильное присваивание. Например, код ниже не скомпилируется:
А вот этот скомпилируется:
И этот код успешно скомпилируется:
Как только компилятор определил значение переменной var, мы не можем присвоить ничего другого, кроме этого типа.
Пункт 7: var не может использоваться для создания экземпляра конкретного типа и назначения его переменной типа интерфейса
В Java мы используем подход «программирование с помощью интерфейсов». Например, мы создаем экземпляр класса ArrayList, связывая его с абстракцией (интерфейсом):
И мы избегаем таких вещей, как привязка объекта к переменной того же типа:
Это наиболее распространенная и желательная практика, так как мы можем легко заменить реализацию интерфейса на любую другую. Для этого лишь необходимо объявить переменную типа интерфейса.
Мы не сможем следовать этой концепции, используя var переменные, т.к. для них всегда выводится конкретный тип. Например, в следующем фрагменте кода компилятор определит тип переменной, как ArrayList :
Есть несколько аргументов в защиту var, которые объясняют подобное поведение:
var используется для локальных переменных, где, в большинстве случаев, программирование с помощью интерфейсов используется меньше, чем в случаях с параметрами методов, возвращаемыми значениями или полями
Область действия локальных переменных должна быть небольшой, поэтому решение проблем, вызванных переключением на другую реализацию, не должно составить больших трудностей
var воспринимает код, стоящий справа, как инициализатор, используемый для определения фактического типа. Если, в какой-то момент, инициализатор будет изменен, то определяемый тип тоже может измениться, вызвав проблемы в коде, опирающемся на эту переменную.
Пункт 8: вероятность вывода неожидаемого типа
Использование var в сочетании с diamond operator (<>) при отсутствии информации для идентификации типа, может привести к неожиданным результатам.
До Java 7 для коллекций использовалось явное указание типов:
Начиная с Java 7 был введен diamond operator. В таком случае компилятор самостоятельно выведет необходимый тип:
Какой же тип будет выведен в коде ниже?
Вы должны избегать подобных конструкций:
Таким образом, var можно использовать только, если мы предоставим необходимую информацию для определения ожидаемого типа. Тип может быть указан непосредственно или передан в качестве аргумента.
Непосредственно указывайте тип:
Передавайте аргументы необходимого типа:
Пункт 9: присвоение массива к var-переменной не требует скобок [ ]
Все мы знаем как объявлять массивы в Java:
Как насчет использования var при работе с массивами? В этом случае нет необходимости использовать скобки с левой стороны.
Избегайте следующего (это даже не скомпилируется):
Код ниже, с использованием var также не скомпилируется. Это происходит потому, что компилятор не может определить тип по правой части:
Пункт 10: var нельзя использовать при объявлении нескольких переменных в одной строке
Если вам нравится объявлять переменные одного типа разом, то вам нужно знать что var не подходит для этого. Следующий код не скомпилируется:
Вместо этого используйте:
Пункт 11: локальные переменные должны стремиться к минимизации своей области видимости. Тип var усиливает это утверждение
Сохраняйте небольшую область видимости для локальных переменных — я уверен, что вы слышали это утверждение до появления var.
Читабельность и быстрое исправления багов — аргументы в пользу этого подхода. Например, давайте определим стэк следующим образом:
Лучше всего это делать так:
Пункт 12: тип var упрощает использование различных типов в тернарных операторах
Мы можем использовать разные типы операндов в правой части тернарного оператора.
При явном указании типов следующий код не скомпилируется:
Тем не менее мы можем поступить так:
Код ниже, также не скомпилируется:
Но можно использовать более общие типы:
Во всех таких случаях лучше предпочесть var:
Из этих примеров не следует, что тип var определяет типы объектов во время выполнения. Это не так!
И, конечно, тип var будет корректно работать при одинаковых типах обоих операндов:
Пункт 13: тип var может быть использован внутри циклов
Мы легко можем заменить явное объявление типов в циклах for на тип var.
Изменение явного типа int на var:
Изменение явного типа Order на var:
Пункт 14: var отлично работает с потоками (stream) в Java 8
Очень просто использовать var из Java 10 с потоками (stream), которые появились в Java 8.
Вы можете просто заменить явное объявление типа Stream на var:
Пример 1:
Пример 2:
Пункт 15: var можно использовать при объявлении локальных переменных, предназначенных для разбиения больших цепочек выражений на части
Выражения с большой вложенностью выглядят впечатляюще и обычно кажутся какими-то умными и важными частями кода. В случае, когда необходимо облегчить читаемость кода, рекомендуется разбить большое выражение, используя локальные переменные. Но иногда написание множества локальных переменных кажется очень изнурительной работой, которую хотелось бы избежать.
Пример большого выражения:
Лучше разбейте код на составные части:
Второй вариант кода выглядит более читабельнее и проще, но первый вариант также имеет право на существование. Для нашего разума абсолютно нормально адаптироваться к пониманию таких больших выражений и предпочесть их локальным переменным. Тем не менее, использование типа var может помочь при разбиении больших конструкций за счет сокращения усилий на объявление локальных переменных:
Пункт 16: var не может быть использован, как тип возвращаемого значения или как тип аргумента метода
Показанные ниже два фрагмента кода не скомпилируются.
Использование var, как тип возвращаемого значения:
Использование var, как тип аргумента метода:
Пункт 17: локальные переменные типа var могут быть переданы, как параметры метода или могут принимать возвращаемое методом значение
Приведенные ниже фрагменты кода скомпилируются и будут исправно работать:
с дженериками все так же будет работать отлично:
Пункт 18: переменные var могут быть использованы с анонимными классами
Вместо явного указания типов:
Используйте var:
Пункт 19: переменные типа var могут использоваться в качестве effectively final переменных
… начиная с Java SE 8, локальный класс может обращаться к локальным переменным и параметрам заключающего блока, которые являются final или effectively final. Переменная или параметр, значение которых никогда не изменяется после их инициализации, являются effectively final.
Что ж, переменные типа var могут быть effectively final. Это можно увидеть в следующем примере.
Пункт 20: var-переменные могут быть final-переменными
Изначально значение var переменной может быть изменено (за исключением, когда она объявлена как effectively final). Но мы можем объявить переменную, как final.
Пункт 21: лямбда выражениям и ссылкам на методы нужны явные типы
Тип var не может использоваться, если невозможно определить конечные типы. Таким образом инициализация через лямбда выражения и ссылки на методы, с использованием var, не скомпилируется:
Вместо этого используйте:
Но в Java 11 разрешено использовать var-переменные в контексте лямбда выражений. Следующий пример кода заработает в Java 11:
Пункт 22: инициализировать var null’ем запрещено
Запрещено объявлять var-переменные без инициализации.
Этот код не скомпилируется (попытка присвоить null):
И этот тоже не скомпилируется (отсутствует инициализатор):
А этот код скомпилируется и будет исправно работать:
Пункт 23: тип var нельзя использовать в полях класса
Вы можете использовать var для локальных переменных, но не в качестве полей классов.
Это ограничение приведет к ошибкам компиляции:
Используйте такой способ:
Пункт 24: var нельзя использовать в блоке catch
Тем не менее, это разрешено в try-with-resources
Блок catch
Когда код бросает исключение, мы должны поймать его, используя конкретный тип.
Следующий код вызовет ошибку компиляции:
В таком случае необходимо использовать явный тип исключения:
Try-with-resources
Однако, var отлично работает в блоке try-with-resources.
Можно заменить кодом с var:
Пункт 25: тип var можно использовать с дженериками
Например, у нас есть следующий код:
В этом случае, использование var работает как и ожидалось, так что мы просто можем заменить T на var:
Давайте взглянем на другой пример, где мы можем успешно использовать var:
Тут можно безопасно заменить List на var:
Пункт 26: будьте внимательны с типом var при использовании Wildcards (?), ковариантов и контрвариантов
Использование? Wildcards
Можно безопасно использовать var таким образом:
Но не заменяйте Foo на var только потому, что вы имеете ошибки в коде, а с использованием var они чудесным образом исчезают.
Давайте рассмотрим следующий пример кода, он не захватывающий, но, думаю, основную идею вы поймете. Исходя из первой строки можно сделать предположение, что вы пытались определить ArrayList из строк, а в итоге получили Collection :
Использование ковариантов (Foo ) и контрвариантов (Foo )
Мы знаем, что можно сделать следующее:
Если мы ошибочно присвоим неверный тип и получим ошибки во время компиляции, это будет именно то, чего мы ожидаем:
Но при использовании var:
Теперь мы можем назначить переменным любой класс, и наш код будет скомпилирован. Но это не то чего мы хотели – наши переменные не имеют ограничений:
Заключение
В этой статье мы рассмотрели тип «var», который появился в Java 10. Также разобрали множество примеров, которые демонстрируют преимущества и недостатки при использовании динамического выведения типа переменных. И наконец узнали, что проверка типов при применении var осуществляется во время компиляции, что позволяет отлавливать множество ошибок.
Используйте var и да прибудет с вами Java!