Первый контакт с «var» в Java 10

Представляю вашему вниманию перевод статьи First Contact With ‘var’ In Java 10 автора Nicolai Parlog.

image

Java 10 будет выпущен 20 марта 2018 года, и все фичи, которые должны быть в этом релизе, уже объединены в основную ветку разработки. Одним из самых интересных нововведений Java 10 безусловно является вывод типа локальной переменной (JEP 286). Это дает вам возможность сократить объявления переменных используя новое ключевое слово var:

var users = new ArrayList<User>();

И это все, спасибо за внимание!

Нет, я уверен, что вам интересно узнать больше. Под катом я расскажу, где применяется var, а где нет, как это влияет на читаемость кода и что произошло с val.

Замена объявлений типа с помощью var


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

URL codefx = new URL("http://codefx.org");

Мы также часто объявляем типы переменных, которые используются только один раз:

URL codefx = new URL("http://codefx.org");
URLConnection connection = codefx.openConnection();
Reader reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

Это не особенно страшно, но всё же несколько избыточно. И хотя IDE могут помочь в написании такого кода, читаемость страдает, когда имена переменных перескакивают вправо-влево, потому что названия типов имеют разную длину или когда разработчики избегают объявления промежуточных переменных, потому что объявления типов будут отвлекать на себя много внимания, не принося никакой пользы.

Начиная с Java 10 у разработчиков появится альтернатива — они могут позволить компилятору вывести тип с помощью var:

var codefx = new URL("http://codefx.org");
var connection = codefx.openConnection();
var reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

При обработке var, компилятор просматривает правую часть объявления, так называемый инициализатор и использует его тип для переменной. И это нужно не только для внутренних расчётов, полученный тип будет также записан в итоговый байт-код.

Как вы видите, это экономит несколько символов при наборе текста, но, что более важно, он дедуплицирует избыточную информацию и аккуратно выравнивает имена переменных, что облегчает их чтение. Стоимость очевидна: некоторые типы переменных, например connection, не сразу очевидны. IDE могут, конечно же, показывать их по требованию, но это не помогает ни в какой другой среде (например, при просмотре кода в браузере — на stackoverflow или даже в этой статье — прим. перев.).

Кстати, если вы беспокоитесь о конфликтах с методами и переменными с именем var: не нужно. Технически, var — это не ключевое слово, а зарезервированное имя типа, то есть его можно использовать только в тех местах, где компилятор ожидает имя типа, но во всех остальных местах он является допустимым идентификатором. Это означает, что только классы, называемые var, больше не будут работать, но это не особо частый случай.

Вывод типа локальной переменной выглядит как простая функция, но это обманчиво. Возможно, у вас уже есть некоторые вопросы:

  • хм, это Java или JavaScript?
  • где я могу это использовать?
  • не ухудшится ли читаемость кода?
  • почему не val или let?

Давайте рассмотрим их подробней.

Нет, это не JavaScript


Я хочу начать с подчеркивания того, что var не изменяет приверженность Java статической типизации ни на йоту. Компилятор отображает все задействованные типы и помещает их в файлы классов, как если бы вы их вводили сами.

Например, вот результат декомпиляции IntelliJ (фактически Fernflower) файла класса с примером URL:

URL codefx = new URL("http://codefx.org");
URLConnection connection = codefx.openConnection();
BufferedReader reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

Это байт в байт тот же результат, как если бы я сам объявил типы. Фактически вывод типов существует только во время компиляции и никак не влияет на итоговый байт-код, что также означает отсутствие какого-либо влияния на производительность. Так что расслабьтесь, это не Javascript, и никто не собирается превращать 0 в бога.

Если вы все еще обеспокоены тем, что отсутствие явных типов делает код хуже, у меня есть вопрос для вас: вы когда-нибудь писали лямбда-выражения, не определяя типы аргументов?

rhetoricalQuestion.answer(yes -> "уловили мысль?");

Где использовать var (и где не нужно)


Название JEP 286, «вывод типа локальной переменной», немного намекает на то, где var можно использовать: для локальных переменных. Точнее, для «локальных объявлений переменных с инициализаторами», так что следующий код работать не будет:

// неа
var foo;
foo = "Foo";

Код должен быть таким: var foo = ''Foo''. Даже тогда это распространяется не на все случаи, так как var не будет работать с так называемыми «poly expressions», такими как лямбда-выражения и ссылки на методы, тип которых компилятор определяет в отношении ожидаемого типа:

// ничто из этого не работает
var ints = {0, 1, 2};
var appendSpace = a -> a + " ";
var compareString = String::compareTo

Единственное подходящее место, помимо локальных переменных — это цикл for:

var numbers = List.of("a", "b", "c");
for (var nr : numbers)
    System.out.print(nr + " ");
for (var i = 0; i < numbers.size(); i++)
    System.out.print(numbers.get(i) + " ");

Это означает, что поля, сигнатуры методов и выражения catch все еще требуют ручного объявления типа.

// неа
private var getFoo() {
    return "foo";
}

Устранение ошибок «Действие на расстоянии»


То, что var может использоваться только локально, является не техническим ограничением, а конструктивным решением. Конечно, было бы неплохо, если бы он работал так:

// держим кулачки за то, чтобы компилятор вывел List<User>
var users = new ArrayList<User>();
// но нет, здесь ошибка компиляции:
users = new LinkedList<>();

Компилятор мог бы легко просмотреть все присвоения и вывести наиболее конкретный тип, который подходит для каждого из них, но он не делает этого. Команда JDK хотела избежать ошибок «действия на расстоянии», что означает, что изменение кода в некотором месте не должно приводить к, казалось бы, несвязанной ошибке далеко в другой части системы.

В качестве примера рассмотрим следующее:

// выведено как `int`
var id = 123;
if (id < 100) {
    // очень длинная ветвь
    // к сожалению не имеет собственного метода
} else {
    // ох, ещё больше кода...
}

Пока что всё идёт… Я не хочу говорить «хорошо», но вы знаете, что я имею в виду. Я уверен, что вы видели такой код. Теперь добавим эту строку:

id = "124"

Что произойдет? Это не риторический вопрос, подумайте об этом.

Ответ заключается в том, что if-условие вызывает ошибку, потому что id больше не будет int и поэтому не может сравниться с 100. Эта ошибка находится на довольно большом расстоянии от изменения, вызвавшего ее. Кроме того, это непредвиденное следствие простого присваивания значения переменной.

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

Почему не могут быть выведены типы полей и методов?


Поля и методы имеют гораздо большую область видимости, чем локальные переменные, и поэтому расстояние между изменениями и ошибками значительно возрастает. В худшем случае изменение типа параметра метода может привести к двоичной несовместимости и, следовательно, ошибкам времени выполнения. Это довольно экстремальное следствие изменения некоторых деталей реализации.

Так как не приватные поля и методы становятся частью контракта класса, и поскольку они не должны просто так меняться, их типы не выводятся. Конечно, исключение можно было бы сделать для приватных полей или методов, но это сделало бы вывод типов довольно запутанным.

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

Предпосылки появления var


Давайте посмотрим за кулисы и узнаем, почему был введен var, как он должен повлиять на читаемость и почему нет val (или let), сопровождающего его. Если вас интересует наиболее подробная информация, посмотрите дискуссии JEP 286, часто задаваемые вопросы и список рассылки Project Amber.

Но зачем?!


Java склонна быть довольно многословной, особенно по сравнению с более молодыми языками, это является одним из слабых мест языка и общей темой для критики новичками и опытными разработчиками Java. Project Amber, в рамках которого был разработан var, направлен на «изучение и инкубацию небольших, ориентированных на продуктивность разработки функций Java-языка», и цель состоит в том, чтобы в целом сократить рутину, связанную с написанием и чтением кода на Java.

Вывод типа локальной переменной согласуется с этой целью. С точки зрения написания кода, это явно упрощает объявление переменных, хотя я бы предположил, что добрая половина моих объявлений генерируется средой IDE, либо во время рефакторинга, либо потому что проще написать вызов конструктора или метода, а затем создать для него переменную.

Помимо упрощения объявлений это также делает их более податливыми. Что я имею в виду? Объявления могут быть довольно уродливыми, особенно если речь идет о названиях обобщённых классов в корпоративных приложениях.

InternationalCustomerOrderProcessor<AnonymousCustomer, SimpleOrder<Book>> orderProcessor = createInternationalOrderProcessor(customer, order);

Это чертовски длинное имя типа, которое выталкивает имя переменной в конец и оставляет вас либо с растянутой до 150 символов строкой, либо инициализацией переменной в новой строке. Оба варианта отстой, если вы нацелены на удобочитаемость.

var orderProcessor = createInternationalOrderProcessor(customer, order);

С var это гораздо менее обременительное и простое для глаз объявление промежуточных переменных, и мы могли бы сделать это в тех местах, где раньше не делали. Подумайте о вложенных или последовательных выражениях, которые вы решили не разбивать, потому что уменьшение их сложности компенсировалось увеличением числа рутинных действий. Разумное использование var может сделать промежуточные результаты более очевидными и более доступными.

Короче говоря, var — это про сокращение многословия и рутины, а не об экономии символов.

А что относительно читаемости?


Теперь перейдем к читаемости. Несомненно, когда типы отсутствуют, читаемость должна ухудшиться, не так ли? Вообще говоря, да. Когда вы пытаетесь понять, как работает часть кода, типы являются важным компонентом. И даже если бы IDE разработали функции, позволяющие отображать все выведенные типы, это все равно было бы более косвенным, чем если бы они всегда присутствовали в исходнике.

Таким образом, var сразу приносит нам недостаток читаемости и должен компенсировать это. Один из способов — выравнивание имен переменных:

// с явными типами
No no = new No();
AmountIncrease<BigDecimal> more = new BigDecimalAmountIncrease();
HorizontalConnection<LinePosition, LinePosition> jumping =
    new HorizontalLinePositionConnection();
Variable variable = new Constant(5);
List<String> names = List.of("Max", "Maria");
 
// с выведенными типами
var no = new No();
var more = new BigDecimalAmountIncrease();
var jumping = new HorizontalLinePositionConnection();
var variable = new Constant(5);
var names = List.of("Max", "Maria");

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

С var имена переменных выходят на первый план и выделяются так, как раньше этого не делали, особенно если подсветка кода отмечает ключевое слово и, таким образом, позволяет инстинктивно игнорировать его. Я какое-то время проводил час или два в день, читая Kotlin, и я тут же привык к этому. Это может значительно улучшить читаемость.

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

Поиск стиля


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

Брайан Гетц (Brian Goetz), архитектор языка Java в Oracle и ответственный за Project Amber, дал первую эвристику:
Используйте конструкцию var, когда она делает код более понятным и более кратким, и вы не теряете существенную информацию.
В связи с этим я надеюсь, что IDE не будут вообще предупреждать нас, если объявление типа может быть заменено на var. Это не универсальная конструкция, как лямбда-выражения.

Почему нет val/let?


Многие языки с var также предлагают дополнительное ключевое слово для неизменяемых переменных. Обычно это называется val, иногда let, но Java 10 не имеет ни того, ни другого, и вместо этого мы должны использовать final var. Вот несколько причин:

  • неизменяемость важна, но для локальных переменных она важна в меньшей степени
  • начиная с Java 8 у нас есть концепция эффективного final, уже и без того приближающая нас к неизменяемым локальным переменным
  • там, где var получил всеобщее одобрение (74% категорически, 12% умеренно) ответная реакция как на var / val, так и на var / let была весьма неоднозначной

Я согласен с первыми двумя пунктами и должен принять последний, но я все еще считаю результат немного разочаровывающим. Наличие val или let рядом с var облегчит напряженность между теми разработчиками, которые ставят final на все, и теми, которые потрясены многословием.

Чтож, возможно в будущем… До тех пор мы должны использовать final var.

Подводя итоги


При объявлении локальных переменных вы можете использовать var вместо имени класса или интерфейса, чтобы позволить компилятору вывести тип. Это работает только в том случае, если переменная немедленно инициализируется, например, как в var s = "". Индексы для циклов также могут быть объявлены с помощью var. Тип, выводимый компилятором, помещается в байт-код, поэтому во время выполнения ничего не меняется — Java все еще является статически типизированным языком.

Помимо локальных переменных, например в полях и сигнатурах методов, var не может применяться. Это было сделано, чтобы избежать ошибок «действия на расстоянии» и сохранить место использования выведенной переменной рядом с местом объявления, что смягчает опасения, связанные с читабельностью.

Хотя бездумное использование var может сделать код хуже, тем не менее это шанс для Java-разработчиков написать более читабельный код, найдя новый баланс между шумом объявлений и сложностью вложенных / последовательных выражений.
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну, и что?
Реклама
Комментарии 181
  • +46
    ахахаха

    Простите братья джависты, вырвалось.
    • +1
      А перегрузку операторов уже завезли?
      • +3
        При создании java разработчики этого языка(которые делали его на основании самых удачных(с их точки зрения) идей c++) считали, что перегрузка операторов — неудачный инструмент и, соответственно, исключили его.

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

        Хоть я и пишу на java, но планирую для саморазвития освоить на достаточном уровне с++ как раз по той причине, что с++ полностью доверяет разработчику инструмент, даже если он может быть опасен.

        Единственное что меня сильно раздражает в с++ так этот то, что нет нормального аналога maven или cargo(rust)
        • 0
          на данный момент в мире c++ в качестве пакетных менеджеров используются разве что vcpkg и conan
          • –2
            к сожалению, перегрузка операторов является очень хорошей абстракцией, если говорить о математике. По этой единственной причине я не считаю Java серьезным языком программирования.
            • –1
              серьезным языком программирования.

              В контексте математики?

              • 0
                В контексте программирования, где очень большие классы задач содержат работу с математикой.
                • +1
                  В конексте практического программирования, перегрузка операторов часто может оказаться «бомбой замедленного действия», срабатывающей как только нужно добавить альтернативную реализацию того же оператора.
                  • 0
                    Спорим, что вы только слышали об этом, а в реальных условиях не встречались? Приведите примеры, пожалуйста. Я видел разные баги отложенного действия, но чтобы перегруженные правильно операторы были «бомбой» — это звучит абсурдно.
                    • +1
                      Повезло не встречаться, я по возможности не использую потенциально опасные практики.
                      Абстрактный пример могу привести. Например, есть класс Date, и для него переопределён оператор +, где второй аргумент — int, чтобы прибавлять дни.
                      В какой-то момент понадобилось добавлять к дате не дни, а секунды. Придется писать метод Date.addSeconds, потому что + int уже переопределен. Тогда чтобы добавить один день, нужно будет писать date += 1, а чтобы добавить секунду date.addSeconds(1), и постоянно помнить об этом.
                      • +1
                        Как мне кажется, здесь проблема не в перегрузке операторов, а в том что вы пытаетесь прибавить тёплое к зелёному и получить что-то осмысленное. К дате нужно прибавлять interval, тогда таких проблем не будет. Ну а если вас поставили перед фактом, то, например в Haskell, я бы определил оператор @ для прибавления секунд: + — дни, @ — секунды. Да, это костыль, но он появился не из-за перегрузки операторов, а из-за неправильного дизайна в целом.

                        Вообще, для повседневных задач, типа получения/вывода данных, рисования формочек и всякой бизнес-логики, перегрузка операторов не особо применима. А вот для математики это круто! Далеко за примером ходить не надо: работа с BigDecimal в Java сейчас это боль. С перегрузкой операторов это было бы на порядок проще и наглядней.
                        • +2
                          В целом, выходит, что операторы приемлемы только при работе с объектами, которые семантически являются числами. И в большинстве случаев «примитивных» типов чисел хватает. А между необходимостью в редких случаях писать a.add(b) или sum(a, b) вместо a + b и вероятностью столкнуться с тем, что я описал (а также, например, внезапным обнаружением, что a + b != b + a), я бы всё же предпочел первый вариант
                          • 0
                            ну а матрицы? a * b != b * a
                            • 0
                              Ещё оператор сложения можно использовать на коллекциях. И на датах при правильном дизайне — `Date() + 3.days + 17.seconds`. И на строках.
                              Оператор умножения в общем-то вообще можно на чём угодно использовать, вопрос в семантике.
                          • 0
                            Как мне кажется, здесь проблема не в перегрузке операторов, а в том что вы пытаетесь прибавить тёплое к зелёному и получить что-то осмысленное.

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


                            p.s. не считаю перегрузку операторов чем-то обязательным и необходимым, что по поводу абстракций — опять же не вижу принципиальной разницы с сообщениями (obj1.plus(obj1) вместо obj1 + obj2). Хотя то как это реализовано скажем в python мне лично нравится (где оператор это просто функция).

                          • +1
                            Правильная архитектура это отдельные типы для секунд, дней, месяцев, и так далее. Тогда и перегрузка операторов будет делать ровно то, что нужно.
                            • –1

                              Увы primitive obsession настолько распространенный код смэл, что опасность остается. Но в целом в таком варианте я допускаю пользу.

                              • +1

                                Если есть удобные фабричные методы для создания интервалов в разных единицах, зачем отдельные типы?

                                • 0
                                  Если мы хотим что-то складывать, то единицы измерения важны. Чтобы не складывать граммы и сантиметры.
                                  • 0

                                    Конечно, я не предлагаю заводить фабричный метод для создания временного интервала из граммов. Важно не путать разные величины, а вот в каких единицах задано значение — неважно. Что-то типа TimeDelta::fromSeconds, TimeDelta::fromMonths и так далее, возвращающие один и тот же тип (с типом для массы не совместимый). Нетрудно придумать единообразное внутреннее представление, с которым перегрузка будет работать единообразно. Иначе говоря, 24 часа и сутки — это ровно одно и то же. А вот какая польза от отдельных типов для интервала в секундах и интервала в минутах — неясно.

                                    • 0
                                      Все верно, тип один — временной интервал, единицы измерения — те же. Я имел ввиду, что не следует ожидать правильной работы программы вообще, например, вызовов перегруженных функций (вот уж где реальная проблема и потенциальная опасность!), когда мы опускаемся до уровня абстракции атомарных типов данных (int, float, и так далее). Конечно же, вы привели именно тот вариант, который и используется в нормальных библиотеках для работы со временем и временными интервалами. Я лишь хотел подчеркнуть: если вам кажется, что перегрузка операторов зло, вы просто не умеете ее готовить!
                                      • 0

                                        Ага, договорились. Замечу, что вы выше предлагали "отдельные типы для секунд, дней, месяцев, и так далее".

                                        • 0
                                          И еще раз, я это сказал лишь для демонстрации того, что в данном случае оперировать голыми int'ами не очень-то правильно. В с++ так вон вообще какое есть en.cppreference.com/w/cpp/language/user_literal
                              • 0
                                я по возможности не использую потенциально опасные практики.

                                Да программировать вообще опасно, если разобраться. Шаг влево, шаг вправо — exception.

                              • 0
                                Я как-то писал expression templates, и в SFINAE-блоке использовался оператор ||, который я в тот момент и пытался перерузить. Получилась вполне себе
                                бомба

                              • 0

                                кстати, это универсальная фраза!
                                можно так


                                В конексте практического программирования, перегрузка операторов наследование методов часто может оказаться «бомбой замедленного действия», срабатывающей как только нужно добавить альтернативную реализацию того же оператора того же метода.
                    • –1
                      Понятно, что это потуги, чтобы заманить побольше разработчиков на удобный язык, но необольщайтесь — сила Java в JVM, там же и слабость.

                      С этими новыми keywords другая проблема — в их освоении и понимании, пока в Java терпимое количество keywords, но если так пойдет дальше, то точно эволюционирует в JavaScript.

                      • 0
                        Поздно!

                        Исходя из этой таблицы, в JS (ECMAScript 5/6) всего 50 зарезервированных ключевых слов, которые нельзя применять в качестве переменных.
                        А здесь представлены ключевые слова в Java. Их тоже 50. Но! в JS количество слов сокращают, а в Java увеличивают.
                        • 0
                          Но ведь нового ключевого слова не добавили вроде. Вы всё ещё можете писать var var = new Var().
                          • 0
                            var var = var()
                            :)
                            • 0
                              Да, хорошо, что не стали плодить жестких ключевых слов. По большому счёту, ключевые слова — это атавизм. Не могу придумать примера, когда синтаксис языка (Java/JS/C#) может быть воспринят двояко, но, наверно, такие примеры есть. На мой взгляд, удобство ключевых слов заключается в упрощении синтаксического анализа и более внятных ошибках при разборе.
                              Кстати, вот ссылка на контекстно-зависимые ключевые слова в C#: docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/contextual-keywords
                              Список интересен тем, что все новые ключевые слова, добавленные в язык после версии 2.0, являются контекстно-зависимыми. (Не совсем ясно, почему в списке нет select, from и where.)

                              Java идёт тем же путём: чем меньше ключевых слов, тем лучше. Следующий этап — добавление "@" к лексемам для обозначения того, что это не ключевое слово (как это сделано в C#). Надеюсь, до этого не дойдёт, но извечный джавовый мем — называть переменные «clazz», из-за того, что слово «class» зарезервировано — тоже не круто.
                      • –17
                        Сначала лямбды и потоки, теперь это… :'(
                        • +2
                          Хотя var можно использовать для ухудшения кода
                          Что? (:
                          • +2
                            Немного перефразировал это предложение, надеюсь так звучит лучше :)
                            • +3
                              var может ухудшить читаемость кода, когда присваивается через вызов метода. К примеру код —
                              var item = GetItem();

                              не даёт нам точно узнать какой именно тип скрывается под var. Это не страшно при работе в IDE, но при просмотре, к примеру, через браузер в гите уже не узнаешь простым наведением.
                              • 0
                                Вот честно, я бы ноги отрывал за повсеместное использование var. Когда разработчик читает код, ему прежде всего важен тип и название переменной, а потом уже каким образом она инициализируется. var забирает у нас информацию о типе и заставляет наш мозг вычислять эту информацию либо на основе названия, либо на основе компиляции мозгом на лету.

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

                                Есть всего несколько случае, когда var имеет смысл:
                                1. Анонимные типы
                                2. Очень длинное название типа/здоровенный дженерик в результате LINQ
                                3. Длинное название типа/здоровенный дженерик и его очевидная инициализация в правой части выражения с помощью конструктора

                                Все остальное вида:
                                1. for (var i = 0; ...) { };
                                2. var documents = repo.GetDocuments()
                                3. var number = «superNumber»;
                                4. var mySuperNumber = a / b + c;

                                порождает лично у меня адскую попоболь и ручки так и тянутся к решарперу, дабы массово разыменовать эти долбаные varы.

                                Я кончил и закурил.

                                P.S. Тут я про C#, но вас тоже это ждет.
                                • +7
                                  По-моему, все лучше с bluetooth var.
                                  1. for (var i = 0; ...) { };

                                  Настолько частый паттерн, что я не представляю, как на нем можно спотыкаться при чтении.

                                  2. var documents = repo.GetDocuments()

                                  Знание типа не имеет никакой ценности без знания контракта. Тут, возвращаясь к вашим словам немного выше, для получения какого-то эффекта от знания типа, потребуется закэшировать контракт.
                                  В случае, если дело происходит в IDE — вообще без разницы. А с var читается легче.

                                  3. var number = «superNumber»;

                                  Проблема не в var, а в именовании переменных. var в этом плане даже дисциплинирует, я бы сказал. Приходится переменные называть понятно.

                                  var mySuperNumber = a / b + c;

                                  decimal mySuperNumber = a / b + c; — намного хуже, по-моему. Есть вероятность ошибиться получить неявное приведение типов, int к decimal, например, и дальше быть уверенным, что все ок.

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

                                  Мне код с var читать удобнее. Если речь про то, чтоб вникнуть в проект, то код читается все равно в IDE, и код, который короче, читается лучше.

                                  Я бы сказал, что есть всего несколько случае, когда var не имеет смысла:
                                  1. На stackoverflow, когда тип невозможно вывести из приведенного кода.
                                  2. В документации при том же условии.
                                  • 0
                                    Настолько частый паттерн, что я не представляю, как на нем можно спотыкаться при чтении.

                                    Частый паттерн для меня for(int i = 0; ..). Чем тут var лучше int? Компактнее, наверное?

                                    Знание типа не имеет никакой ценности без знания контракта. Тут, возвращаясь к вашим словам немного выше, для получения какого-то эффекта от знания типа, потребуется закэшировать контракт.
                                    В случае, если дело происходит в IDE — вообще без разницы. А с var читается легче.

                                    Если вам без разницы Document[], List или вообще IEnumarable, то это печально. Мне лично до определенного момент все равно как получили коллекцию, но мне всегда важно какая именно это коллекция.

                                    Прочее вообще комментировать смысла не вижу.
                                    • +2
                                      Частый паттерн для меня for(int i = 0; ..). Чем тут var лучше int? Компактнее, наверное?

                                      Привычнее на уровне мышечной памяти, только и всего. var я набираю намного чаще, чем int. А о том, чтобы вывести int, пусть позаботится компилятор.

                                      Если вам без разницы Document[], List или вообще IEnumarable, то это печально. Мне лично до определенного момент все равно как получили коллекцию, но мне всегда важно какая именно это коллекция.

                                      Все это было бы здорово, если бы это было на самом деле так. Ниже вы упоминали типы и структуры данных. Вот смотрите:
                                      IEnumerable numbers = new[] {1, 2, 3};
                                      Создание массива можно спрятать за вызов функции для большего драматизма. Какая именно это коллекция в вашем понимании? Какая сложность применимых к ней операций? Никакой информации об этом мы не получили, а написать и прочитать объявление пришлось. О том, что это коллекция, и так понятно из имени переменной. Понятно, вы так не пишете. Наверняка, никто так не пишет, но код меняется со временем, и расставлять себе ловушки на будущее я считаю плохой идеей. Пусть за вывод типов беспокоится компилятор, за подсказки — IDE. Они с этим хорошо справляются, лучше меня.
                                    • –6
                                      Знание типа не имеет никакой ценности без знания контракта.


                                      Кстати, рассказать как увидеть контракт, когда не используется var?

                                      Document[] documents = repo.GetDocuments();

                                      внеееезапно контракт:

                                      Document[] IDocumentRepository.GetDocuments();

                                      а вот c var вы вообще не поймете что там за контракт. Пока у IDE не спросите, конечно.
                                      • +2

                                        Просто так слово Document перед именем переменной не так уж много информации добавляет.


                                        В проекте может быть несколько разных классов с именем Document, лежащие в разных package. Давайте тогда писать org.mycompany.documents.Document, чтобы уж точно все знать, не пользуясь подсказками IDE.

                                        • –1
                                          Ну, обычно понятно в каком пространстве имен мы работаем, нет?
                                          • +1

                                            Если речь идет о вашей собственной БД, то да, понятно.
                                            А если это вызов удаленного сервиса через подключаемую библиотеку, то Document document = ... даст вам примерно столько же информации, сколько и var document = .... Все равно придется смотреть на импорты и переходить по файлам.

                                    • –1
                                      Разработчику плевать на тип, в подавляющем большинстве случаев его интересует только семантика. Если названия ваших методов не несут этой информации — проблема совсем не в var.
                                      • –1
                                        Ну, если вашим разработчикам плевать на типы и структуры данных, то медицина здесь бессильна. А если вы название каждого метода дополняете еще и типом возвращаемых данных, то мягко говоря непонятно зачем вы вообще используете языки со статической типизацией при разработке.
                                      • +2
                                        Автовывод типа (как в var) вообще-то в языках программирования с самого их рождения. Когда вы видите выражение типа A+B, вы не пишете его тип на каждую операцию. Когда вы зовёте f(A+B), вы не пишете тип фактического аргумента функции (и можете нарваться на автоконверсию). Если вы вообще способны работать с языком, в котором есть подобные способности автовыведения типа — то есть со 100% нынешних языков — то в чём разница с тем, что это промежуточное значение получило название (формально, присвоено переменной)?
                                        Точно так же, вы или доверяете транслятору и способностям кодера оценить тип выражения, или не доверяете. Если не доверяете, вы не будете использовать автовывод (var в Java, C#, auto в C++...) Если доверяете — вам автовывод не помешает. Вы, как видно, не доверяете. Ну, тоже вариант. Но хотелось бы понять причины такого недоверия.

                                        Я в общем-то тоже в основном не доверяю :), но auto (C++) в итераторах — очевидно удобная возможность, не мешающая правильному использованию. Здесь будет что-то подобное.

                                        А заабьюзить можно что угодно. Самый дурной вариант, мне кажется, это автосужение множества значений — например, при var автовывод дал int, а затем ему пытаются присвоить float и не предполагают возможность усечения до целого. Вот такое должно быть явно прописано в списке подводных камней.
                                  • +8

                                    Да неужели! К 10ой версии-то! Глядишь станет приятно читать эти полотна.

                                    • +1
                                      А помню, как всем доказывали, что var портит читаемость кода.
                                      • +1
                                        Читаемость кода портит постоянная тавтология типов, особенно в языках, где нужно newкать каждую новую переменную. В c++ auto куда менее нужен, поскольку там есть стековое размещение, и в одной строке два раза тип в половине случаев не указывается, поскольку указания типа для выделения памяти уже достаточно. Хотя в c++ изначально придумали auto для итераторов, они потом придумали foreach, с появлением которого вся эта магия с auto больше не требуется. Хотя чисто из-за неприглядного названия я предпочитаю не использовать его так же, как var в C#. Не делать же #define var auto?
                                        • 0
                                          auto в C++ намного мощнее, чем в Java и C#. Оно там используется не только для вывода типа локальной переменной, но и для вывода возвращаемого значения из функции, вывода типов аргументов для лямбд.
                                    • +2
                                      Java 10? Они же вроде пару месяцев тому назад выпустили 9. Я что-то пропустил?
                                      • –7
                                        Джаву, говорят, отдали на растерзание опен сорсу, так что теперь новая версия каждые полгода
                                        • 0

                                          При чём тут опенсорс?

                                          • +2

                                            И то верно, ввожу тут людей в заблуждение. На самом деле все перепутал с JavaEE

                                            • 0
                                              Вообще то недалеко от правды: Oracle анонсировал, что хочет всё из Oracle JDK передать в OpenJDK. Так что комментарий выше зря заминусовали.
                                        • 0
                                          Так объявили же полугодовые по-моему циклы выпуска новых версий (с оговорками само-собой, что будут стабильные 2-3 годовые версии и еще некоторыми нюансами). Типа сильно долго ждать многие мелкие фичи, которые задерживаются из-за больших основных годами, хотя уже готовы к релизу давно.
                                          • 0
                                            И почему то пока только для 64Бит. Для 32 теперь совсем ничего не будет??
                                            • 0
                                              Да. 32-bit Java — всё :(
                                              Оракл решил не тратить время на тестирование под 32bit платформы.
                                              При желании можно собрать себе OpenJDK под 32bit и получить почти то же самое. Изменений, блокирующих работу на 32bit в Java нет. Только отсутствие официальной поддержки.

                                              PS: Когда Java 9 только релизнулась, на офф. сайте была 32bit версия, но её быстро выпилили и сказали, что выложили по ошибке :)
                                              • 0
                                                Странно, а поддержка железяк вроде интернета вещей. Поддержка старого железа, где о 64 битах не мечтают…
                                                Ведь самый козырь Java (как они сами себя и преподносят) — Java есть везде от кофеварок :-) до автомобилей…
                                                • 0

                                                  Видимо, идея в том, что на старом железе вам вряд ли особо понадобится новый софт на Java. В любом случае — Оракл просто сокращает расходы.

                                                  • 0
                                                    Java ME 8 есть.
                                              • 0
                                                Там произошли изменения по способу формирования релизов. Теперь вместо набора фич, который иногда могут пилить очень долго, будут каждые N месяцев выпускать всё то, что получилось закончить и довести до ума. Так что да, Java теперь будет выходить чаще.

                                                А еще, возможно, это будет не «Java 10». А что-то типа «Java 18.3» (год и месяц).
                                                • 0
                                                  День в день с Win10 походу :-)
                                                  Ждем Java Crazy Spring Update :-)
                                              • +2
                                                Что, уже первое апреля?

                                                В Java же сознательно отказались от type inference. Ещё в версии 1.3 обсуждалось bugs.java.com/bugdatabase/view_bug.do?bug_id=4459053
                                                Humans benefit from the redundancy of the type declaration in two ways.
                                                First, the redundant type serves as valuable documentation — readers do
                                                not have to search for the declaration of getMap() to find out what type
                                                it returns.
                                                Second, the redundancy allows the programmer to declare the intended type,
                                                and thereby benefit from a cross check performed by the compiler.
                                                • +3

                                                  Инференс потихоньку проникал в джаву ещё с пятой версии, становясь с каждой новой версией всё веселее. Видимо, вы отстали от жизни.

                                                  • +3

                                                    Лямбда выражения используют type inference, если что.
                                                    Дженерики тоже, e.g.:


                                                    T foo(T bar) {
                                                    return bar;
                                                    }

                                                    String s = foo("");
                                                  • +4
                                                    Ох, 2018 на дворе, они изобрели auto
                                                    • +5

                                                      Не изобрели, а адаптировали из других языков, после того как другие обкатали. Могло ведь оказаться, что фича ненужная.

                                                      • –10
                                                        Молодцы, что до 2018 хотя бы продержались. Многие уже до этого сдались ради «читаемости».
                                                        • 0
                                                          А читаемость это недостаток, да?
                                                          Вы должно быть пишите на Brainfuck?
                                                      • +3
                                                        Спасибо за статью. Отлично, теперь у нас есть var в Jave. Вот индийцы обрадуются. )).
                                                        • +1
                                                          if (var == true)
                                                          return true;
                                                          else if (var == false)
                                                          return false;
                                                          else
                                                          return (!false && !true);
                                                        • +9
                                                          «Используйте конструкцию var, когда она делает код более понятным и более кратким, и вы не теряете существенную информацию.» Совет на все сто, точно предотвратит плохое использование var. До этого программисты не понимали, как же использовать синтаксический сахар, но теперь наконец-то объяснили. Мог бы еще сказать «Пишите код правильно, читаемо и без ошибок», тогда и про тестирование можно было забыть. Я понимаю, что тут нельзя придумать объективный критерий для использования var, но его совет это смех.

                                                          Персонально, он не нужен. Пусть программист потратит лишние 3 секунды и напишет тип один раз, а я (и другие) зато смогу потом легко понять читая код 100500 раз после этого, что же вернула функция doFoo().
                                                          • 0
                                                            Я тоже считаю, что такие улучшения мне не нужны. Когда я написал тип переменной, то редактор уже сразу понимает, что мне предложить для ее инициализации, так что написание кода с явным указанием типов не составляет труда. А вот читать такой код гораздо легче.

                                                            Но, к сожалению, приходится идти за модой, чтобы не отставать в конкурентной борьбе. Чтобы новые проекты начинали именно на яве, чтобы в нее инвестировали деньги, нужно привлекать побольше новичков. А новички хотят, чтобы было меньше букв.
                                                            • +1
                                                              Я, как C#-программист, тоже скептически относился к var-ам, когда их добавили. Аргументация была ровно как у Вас. Сейчас настолько привык, что без них уже не представляю жизнь.
                                                              • +1
                                                                Как используете? Лично я только с анонимными типами использую, или в случае длинной составной коллекции, но всё таки редко
                                                                • 0
                                                                  Это вопрос вкуса и договорённостей внутри команды. У нас в команде принято использовать var везде, где это возможно, даже для примитивных типов. Иногда приходится явно обозначать тип, и это меня убивает. Например, такой код:
                                                                  IEnumerable<string> result = null;
                                                                  if (someCondition)
                                                                  {
                                                                    result = new List<string>();
                                                                    // логика по добавлению элементов.
                                                                  }
                                                                  else
                                                                  {
                                                                    result = Enumerable.Empty<string>();
                                                                  }
                                                                  

                                                                  Или вот ещё проблема с тернарной записью:
                                                                  int? result = someCondition ? 42 : null;
                                                                  

                                                                  Теоретически, компилятор способен сам вывести тип переменной result, но сейчас этого нет. Думаю, когда-нибудь добавят и это (на сколько знаю, в C#8 проблему с Nullable побороли).
                                                                  Вообще, вывод типов — это ключевая фича всех функциональных языков, а C# и Java к ним постепенно приближаются. В последнем Шарпе уже можно использовать кортежи в качестве возвращаемых значений методов, а в перспективе, думаю, в некоторых случаях можно будет вообще не указывать тип возвращаемых значений или писать «var».
                                                                  • +1

                                                                    Есть ещё такой трюк: var result = default(IEnumerable<string>);

                                                                    • 0
                                                                      Да, как вариант, выглядит неплохо. Но писать дольше :)
                                                                    • +3
                                                                      У нас в команде принято использовать var везде, где это возможно, даже для примитивных типов.

                                                                      А, к примеру,


                                                                      var number = 1L;

                                                                      всегда ли удобнее и читаемее, чем?:


                                                                      long number = 1;

                                                                      И как быть, когда некоторая функция возвращает экземпляр некоторого типа, но мы сознательно хотим редуцировать тип возвращенного значения до базового типа или интерфейса, реализованного в типе возвращаемого значения?
                                                                      Здесь тип придется указать явно, т.к. var сразу выведет точный тип.


                                                                      var result = SomeFunc(); // var == SomeResult

                                                                      или?:


                                                                      ISomeResult result = SomeFunc();

                                                                      А такой код


                                                                      IEnumerable<string> result = null;
                                                                      if (someCondition)
                                                                      {
                                                                        result = new List<string>();
                                                                        // логика по добавлению элементов.
                                                                      }
                                                                      else
                                                                      {
                                                                        result = Enumerable.Empty<string>();
                                                                      }

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


                                                                      Этот код сразу напрашивается на вынесение в метод (или в локальную функцию, если методу при наполнении нужен контекст не класса, а основного метода):


                                                                      IEnumerable<string> GetSomeItems(bool someCondition)
                                                                      {
                                                                          if (!someCondition)
                                                                              return null;
                                                                      
                                                                          var result = new List<string>();
                                                                          // Adding items
                                                                          return result;
                                                                      }

                                                                      Вынесли из основного метода простыни с if/else, сразу видна обработка "плохого" случая, и т.д.
                                                                      Плюс, код легче дорабатывается — проще заменить дефолтное значение результат по сравнению с вариантом без отдельного метода:


                                                                      IEnumerable<string> GetSomeItems(bool someCondition)
                                                                      {
                                                                          if (!someCondition)
                                                                              return Enumerable.Empty<string>();
                                                                      
                                                                          var result = new List<string>();
                                                                          // Adding items
                                                                          return result;
                                                                      }
                                                                      • –1
                                                                        Это код очень легко читается:
                                                                        Вызвали метод.
                                                                        Сохранили данные в переменной.

                                                                        Если зависать над каждой запятой, то это будет не чтением того, что код делает, а проверкой правописания.
                                                                        • +2

                                                                          … Вы про использование var или простыни if-then-else (неужто последние легко читаются)?


                                                                          А что до запятых — программный код формален, и выполняется ровно так, как написано, поэтому, проверять "запятые" таки требуется.


                                                                          На примере с var:
                                                                          Допустим, вам нужна была именно long-переменная, но, не обратив внимания на "запятые", вы написали "var number = 1;" вместо "var number = 1L;".


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


                                                                          Но, скорее всего, ошибка проявится уже в продакшене и будет плавающей и труднодиагностируемой, и не будет приводить к непосредственному исключению:


                                                                          • проверка переполнения в проде отключена, в переменной будут неверные данные, они пойдут куда то дальше, еще не так плохо, если где-то дальше приведут к программному исключению до окончания основного алгоритма;
                                                                          • а ведь может все без исключений отработать и привести к неверным результатам, когда принимается решение в офлайне на основе полученных от программы данных (а если это медицинская или АСУ ТП система?).
                                                                          • 0
                                                                            Про Long согласен — в таких случаях лучше явно указывать тип. Это один из примеров кривоватой лексики языка (особенно учитывая неоднозначности типа 11 versus 1l). Да и в целом числовые суффиксные литералы, на мой взгляд, довольно кривое решение. Для каких-то типов они есть (m, f, d, l, ul), а для каких-то забыли придумать (byte, short, ushort). Странно.
                                                                            Но если речь идёт о bool, string и char, то я всё же предпочитаю использовать var. Зачем писать «bool b = true;», если и так очевидно, что это bool?

                                                                            Что касается моего примера с if-else — да, result не надо инициализировать null-ом. Писал без IDE, пропустил этот момент. Но выносить такое в отдельный метод тоже не всегда правильно, так как это засоряет private scope класса. Вложенные функции решают эту проблему, но их пока не везде можно использовать.
                                                                            • 0
                                                                              Полагаю, что суффиксы m, f, d, l, u, ul сделали, т.к. соответствующие типы имеют отражение в регистрах процессора, а про 8- и 16- битные целочисленные типы «знает» только виртуальная машина (CLR в случае .NET) и при трансляции из байт-кода (IL) в машинный код они всегда расширяются до 32-битного типа.

                                                                              Но тогда платформо-независимость имеет ограничения, т.к. язык предназначен для железа с определенным кругом возможностей.
                                                                              • 0

                                                                                кстати, по аналогии с выше предложенной конструкцией:


                                                                                var result = default(IEnumerable<string>);

                                                                                можно писать так:


                                                                                var number = (long)1;

                                                                                и


                                                                                var number = (byte)1;

                                                                                Так и единообразно var везде пишем, и явно видим приведение к long,
                                                                                а в случае byte так и вовсе это единственный способ использовать var за неимением суффикса.


                                                                                Вот только здесь мы сталкиваемся с code style: сложившейся практики именно так писать не просматривается.

                                                                                • +1
                                                                                  В C# вообще всё не так очевидно c типами. Компилятор пытается вывести тип переменной, исходя из числового значения лексемы. Если, скажем, мы напишем «var x = -2147483648;» то тип будет int, но если убрать минус, то это будет уже uint, т.к. минимальное и максимальное значения отличаются на единицу).

                                                                                  Дальше больше: если мы захотим написать «var y = (double)1000000000000000000000000000;», то будет синтаксическая ошибка. Но если добавить литеру «d» и убрать каст (var y = 1000000000000000000000000000d;), то всё скомпилируется, и типом ожидаемо будет double.
                                                                                  Так что, отказаться от использования суффиксов не получится пока.
                                                                                  • 0
                                                                                    Да, все верно, способ записи лексемы определяет ее тип, когда мы пишем (long)1 или (byte)1, нужно понимать, что 1 — это не абстрактная математическая единица, а именно единица типа int.

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

                                                                                    Получается не очень то и удобно — как мы обсудили в этой ветке, не получается выработать какой-то единый способ присвоения числовых переменных.
                                                                          • 0
                                                                            И как быть, когда некоторая функция возвращает экземпляр некоторого типа, но мы сознательно хотим редуцировать тип возвращенного значения до базового типа или интерфейса, реализованного в типе возвращаемого значения?

                                                                            В принципе, это не такая уж и сложная задача для компилятора — вывести наиболее конкретный обобщённый тип, необходимый в данном контексте. TypeScript успешно с этим справляется. Почему это до сих пор не реализовано в C# — не понятно. Тем более не понятно, почему это не добавили в Java, учитывая, что var ещё только в проекте и можно учесть все минусы существующих языков.
                                                                            • 0
                                                                              Думаю, вывод наиболее конкретного обобщенного типа не сделали в C# по той же причине, почему, как это как раз в статье и объяснено, не сделали в Java — потому что тогда компилятору нужно просматривать много кода «вперед», что само по себе несложно, но усложняет код и приводит к ошибкам.

                                                                              Например, вместо
                                                                              string[] names = GetNames();

                                                                              я пишу
                                                                              var names = GetNames();

                                                                              Сейчас компилятор сразу «знает», что var это string[], IntelliSense может предлагать мне методы этого типа при обращении к names, и т.д.
                                                                              Фактически, это даже не вывод типа, и даже не сахар — тип переменной уже определен в правой части выражения, сразу в момент написания этого выражения.

                                                                              Если же исходить из того, что компилятор должен автоматически максимально редуцировать тип к базовому типу или интефвейсу при выводе, то получается, в зависимости от того, к каким методам names я обратился, компилятор должен выводить string[], IList<string&gt, ICollection<string&gt, IReadOnlyList<string&gt, IReadOnlyCollection<string&gt, IEnumerable<string&gt?

                                                                              На мой взгляд, такой вывод типа будет неочевиден (без всплывающей подсказки при наведении на переменную мы вообще не будем знать тип, если только мысленно не сделаем тот же проход по коду, что и компилятор),
                                                                              да и как IntelliSense сможет предлагать методы names, если тип переменной еще не определен — всегда предлагать, что есть в «максимальном» типе?

                                                                              Также здесь возможны дополнительные сложности, связанные с explicit-реализацией интерфейсов и со множественным наследованием интерфейсов.
                                                                              • 0
                                                                                По-моему, всё гораздо проще. Если после инициализации переменной её не переприсваивают, то можно оставить изначальный тип. А если, скажем, будет такой код:
                                                                                var names = GetNames(); // GetNames возвращает string[]
                                                                                
                                                                                if (dropBob)
                                                                                {
                                                                                  names = names.Where(name => name != "Bob");
                                                                                }
                                                                                

                                                                                то компилятор может понять, что names должен быть общим типом IEnumerable<string>. Не надо анализировать вызываемые методы. Достаточно посмотреть, значения каких типов присваиваются переменной.
                                                                                Естественно, в этом случае свойство Length будет уже недоступно после if-а, т.к. тип может поменяться.
                                                                                • +1

                                                                                  Да, идея понятна.
                                                                                  В общем случае это объявить var даже без первичной инициализации, главное, чтобы потом во всех ветках переменная была проинициализирована, а далее компилятор выведет тип — ближайшего общего предка (с выводом интерфейса вместо типа будут трудности — неясно, чему отдавать предпочтение — интерфейсу или типу, плюс может быть несколько общих интерфейсов).


                                                                                  Идея хорошая, только тогда нужно быть готовым и к таким образчикам:


                                                                                  var value;
                                                                                  if (someCondition)
                                                                                  {
                                                                                    value = 0;
                                                                                  }
                                                                                  else
                                                                                  {
                                                                                    value = "123";
                                                                                  }

                                                                                  Где var будет выведен как object.
                                                                                  И еще будут появляться проверки и приведение value к типу.
                                                                                  Такие и сейчас встречается, но object нужно написать осознанно, а var будет дополнительно провоцировать.
                                                                                  Впрочем, кажется, этот случай уже описан в статье.

                                                                                  • +1
                                                                                    Выстрелить себе в ногу можно было всегда, и это один из примеров, да. При разработке компиляторов надо взвешивать все за и против. Наверняка, вопрос пустых var-ов поднимался, и было принято решение пока оставить всё как есть. Лично я был бы согласен терпеть неоднозначности вроде той, что вы привели (благо, они не выходят за пространство имён метода).

                                                                                    P.S. Прошу прощения за долгий ответ. Читал ветку с имейлов, не мог ответить сразу, забыл ответить потом.
                                                                      • +1
                                                                        Человек ко всему привыкает. Я думаю, вы и без var прекрасно бы писали хорошие программы на C#. Я, например, вообще код справа налево пишу. Сначала набираю new URL("..."), затем выделяю строку и запускаю горячей клавишей команду Extract local variable. Через мгновение у меня уже URL url = new URL("..."). Даже не представляю, что должно произойти, чтобы я пристрастился к форме var url = new URL("..."). Бывает иногда лень среду поднимать, поэтому пишу программу в блокноте. Вот там может быть var сыграет.
                                                                        • 0
                                                                          Сейчас попробовал сделать то же самое в VS, думаю «чем чёрт не шутит?» :)

                                                                          Так такие вещи как
                                                                          var url = new URL("...");

                                                                          как раз таки и не рекомендуется писать, смысла же нет никакого.

                                                                          А вот в случае
                                                                          Dictionary<string, Dictionary<string, Func<int, float, string>>>

                                                                          я лучше напишу var
                                                                          • 0
                                                                            А вот, скажем, есть метод ():
                                                                            double Gizmo(double arg)
                                                                            {
                                                                              double arg2 = arg * arg;
                                                                              return Math.Sign(arg2) * Math.Truncate(arg2);
                                                                            }
                                                                            

                                                                            И вдруг мы решили вместо double использовать decimal. Если бы мы сразу написали var arg2, то рефакторинг был бы минимальным — надо было бы только переписать сигнатуру метода. А теперь придётся лезть в тело метода и изменять все double на decimal. В этом примере всё не так страшно — всего одно изменение, но если это был бы какой-то сложный расчёт, пришлось бы помучиться.
                                                                            • 0
                                                                              Это кстати для меня новый кейс. Но тогда реально придется всё var обозначать, что лично мне, скорей всего, будет не очень удобно. С другой стороны, возможно неудобства будет меньше, если тщательно именовать переменные, что в принцыпе в обоих случаях отличная практика. Дело привычки и договорённости — это верно.
                                                                              • +1
                                                                                С другой стороны, возможно неудобства будет меньше, если тщательно именовать переменные, что в принцыпе в обоих случаях отличная практика.

                                                                                Осторожно! Вы в одном шаге от венгерской нотации.
                                                                                • +1
                                                                                  От карго-культа по мотивам венгерской нотации, если точнее.

                                                                                  Венгерка — она не про то, чтобы именовать переменные согласно их типам (это не имеет смысла), она про то, чтобы именовать их согласно семантике. Ну, чтобы флоат с x-координатой пикселя отличать от флоата с r-компонентой цвета пикселя, например.

                                                                                  Но чем более выразительная система типов в вашем языке, тем больше вам это не нужно.
                                                                                  • 0
                                                                                    Не только. Я бы сказал, что венгерка — это о любой дополнительной информации, содержащейся в имени. Области видимости, например.
                                                                              • +1

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


                                                                                С другой стороны, неоднозначный — с учетом специфики типов с фиксированной и плавающей точкой в реальном проекте все равно было бы много кода, завязанного на именно специфику типов (диапазон, точность, особенности сравнения — через эпсилон или непосредственное, и т.д.).


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

                                                                                • +3

                                                                                  Наоборот хорошо, везде сходите, посмотрите, не отломалось ли чего. В разных местах нужно по-разному поступить, где-то заменить на decimal, где-то сразу в double сконвертировать.

                                                                              • 0
                                                                                На сколько помню, в IDEA можно написать «new URL(...).new» и нажать Tab, чтобы она создала новую переменную и сама сгенерировала имя. Решарпер умеет так же, вместо типа подставляет var (можно настроить, чтобы подставлял тип).
                                                                                • 0

                                                                                  По дефолту в Intellij это через new URL("...").var работает

                                                                            • 0

                                                                              Я переучивался на С# после 8 лет Java и по началу конечно был возмущен var'ами, мол, не видно же какой тип! Все это вопрос привычки, тем более сейчас, когда многие языки имеют аналогичный функционал, даже JS/TS.

                                                                          • +3
                                                                            так глядишь, java на c# совсем похожа станет)
                                                                            • 0

                                                                              Осталось завезти свойства, перегрузку операторов, делегаты c событиями… Делагаты очень хорошая вещь, иначе все превращается в явный Observer с ISomethingListener. С лямбдами лучше, но не идеально.


                                                                              … async/await, кортежи, Pattern matching (хотя мне не нравится его реализация в C# 7)...

                                                                              • +4
                                                                                Java с C#-овским синтаксическим сахаром — это Kotlin
                                                                                • 0

                                                                                  Да C# до Kotlin будет путь побольше, чем Java 8/9 до C#.


                                                                                  В Kotlin есть Null Safety, им/мутабельность переменных (var/val), полноценные свойства (back-field, видимый только в геттере/сеттере, + механизм делегированных свойств), when (нормально юзабельный и читабельный, в отличие от switch/case/break), более выразительный механизм расширения методов через лямбда-стратегии (см. пример, как вызывается extension use для AutoClosable), более адекватные умолчания для областей видимости, и много другого.


                                                                                  Многое из этого не может быть реализовано в C# с сохранением обратной совместимости (а без нее это будет уже не C#, а аналог Kotlin для .NET).


                                                                                  А чтобы подтянуть Java до текущего состояния C#, нужно не так много — как тут уже писали — свойства как таковые, делегаты+события, async/await, и немного по мелочи.
                                                                                  Если ставить задачу дотянуть Java до C#, а не до Kotlin, и не ломать обратную совместимость, то это все реально — языки и платформы очень похожи, нужно доделать недостающее.

                                                                              • +4
                                                                                Компилятор отображает все задействованные типы и помещает их в файлы классов, как если бы вы их вводили сами.
                                                                                Например, вот результат декомпиляции IntelliJ (фактически Fernflower) файла класса с примером URL:

                                                                                Это байт в байт тот же результат, как если бы я сам объявил типы.

                                                                                Результат работы декомпилятора не может быть доказательством «помещения» чего-либо куда бы то ни было.
                                                                                Для исполнения программы информация о типе локальных переменных в class-файле не нужа и потому если и присутствует, то только в отладочной информации. Которой может и не быть.


                                                                                Fernflower выводит типы локальных переменных сам, не полагаясь на наличие отладочной информации. Хотя и использует её в ряде случаев.


                                                                                Простейший пример:


                                                                                public class Test {
                                                                                
                                                                                    public static void main(String... args) {
                                                                                
                                                                                        Object greeting = "Habrahabr, world!";
                                                                                        System.out.println(greeting);
                                                                                
                                                                                    }
                                                                                
                                                                                }

                                                                                Декомпилятор в IDEA будет уверен, что декларировали мы переменную с типом String:


                                                                                //
                                                                                // Source code recreated from a .class file by IntelliJ IDEA
                                                                                // (powered by Fernflower decompiler)
                                                                                //
                                                                                
                                                                                public class Test {
                                                                                    public Test() {
                                                                                    }
                                                                                
                                                                                    public static void main(String... var0) {
                                                                                        String var1 = "Habrahabr, world!";
                                                                                        System.out.println(var1);
                                                                                    }
                                                                                }

                                                                                Помимо локальных переменных, например в полях и сигнатурах методов, var не может применяться.

                                                                                В предложении слов порядок, Йода магистр одобрил бы.

                                                                                • –3
                                                                                  1. Ваш код компилятор не соберет.
                                                                                  2. String в декомпилированном примере, скорее всего явно выводит javac. Fernflower тут вообще ни при чём.
                                                                                  • +1
                                                                                    Ваш код компилятор не соберет.

                                                                                    Вас не смущает, что чуть ниже идёт листинг декомпилированного class-файла, который по вашему мнению даже и не скомпилируется?


                                                                                    String в декомпилированном примере, скорее всего явно выводит javac. Fernflower тут вообще ни при чём.

                                                                                    Постойте, но ведь в предыдущем пункте вы утверждали, что «компилятор не соберет», а теперь утверждаете, что не только соберёт, но ещё и «выведет».
                                                                                    Вот что показывает javap:


                                                                                    javap -c -s -p -v Test.class
                                                                                    Classfile /C:/Temp/Test.class
                                                                                      Last modified 08.01.2018; size 425 bytes
                                                                                      MD5 checksum d5aeccb9b28d3f897d76c7174e7e18cf
                                                                                      Compiled from "Test.java"
                                                                                    public class Test
                                                                                      minor version: 0
                                                                                      major version: 52
                                                                                      flags: ACC_PUBLIC, ACC_SUPER
                                                                                    Constant pool:
                                                                                       #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
                                                                                       #2 = String             #16            // Habrahabr, world!
                                                                                       #3 = Fieldref           #17.#18        // java/lang/System.out:Ljava/io/PrintStream;
                                                                                       #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/Object;)V
                                                                                       #5 = Class              #21            // Test
                                                                                       #6 = Class              #22            // java/lang/Object
                                                                                       #7 = Utf8               <init>
                                                                                       #8 = Utf8               ()V
                                                                                       #9 = Utf8               Code
                                                                                      #10 = Utf8               LineNumberTable
                                                                                      #11 = Utf8               main
                                                                                      #12 = Utf8               ([Ljava/lang/String;)V
                                                                                      #13 = Utf8               SourceFile
                                                                                      #14 = Utf8               Test.java
                                                                                      #15 = NameAndType        #7:#8          // "<init>":()V
                                                                                      #16 = Utf8               Habrahabr, world!
                                                                                      #17 = Class              #23            // java/lang/System
                                                                                      #18 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
                                                                                      #19 = Class              #26            // java/io/PrintStream
                                                                                      #20 = NameAndType        #27:#28        // println:(Ljava/lang/Object;)V
                                                                                      #21 = Utf8               Test
                                                                                      #22 = Utf8               java/lang/Object
                                                                                      #23 = Utf8               java/lang/System
                                                                                      #24 = Utf8               out
                                                                                      #25 = Utf8               Ljava/io/PrintStream;
                                                                                      #26 = Utf8               java/io/PrintStream
                                                                                      #27 = Utf8               println
                                                                                      #28 = Utf8               (Ljava/lang/Object;)V
                                                                                    {
                                                                                      public Test();
                                                                                        descriptor: ()V
                                                                                        flags: ACC_PUBLIC
                                                                                        Code:
                                                                                          stack=1, locals=1, args_size=1
                                                                                             0: aload_0
                                                                                             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
                                                                                             4: return
                                                                                          LineNumberTable:
                                                                                            line 1: 0
                                                                                    
                                                                                      public static void main(java.lang.String...);
                                                                                        descriptor: ([Ljava/lang/String;)V
                                                                                        flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
                                                                                        Code:
                                                                                          stack=2, locals=2, args_size=1
                                                                                             0: ldc           #2                  // String Habrahabr, world!
                                                                                             2: astore_1
                                                                                             3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
                                                                                             6: aload_1
                                                                                             7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
                                                                                            10: return
                                                                                          LineNumberTable:
                                                                                            line 5: 0
                                                                                            line 6: 3
                                                                                            line 8: 10
                                                                                    }
                                                                                    SourceFile: "Test.java"

                                                                                    Покажите мне, пожалуйста, где здесь «явный вывод» или хотя бы хоть какое-нибудь упоминание типов локальных переменных.

                                                                                    • +1
                                                                                      Браво, вы исправили ошибку компиляции в своем предыдущем комментарии, а потом минусанули меня. Будьте честны для начала с самим собой.

                                                                                      В вашем выводе javap — Object, все честно. Тут вынужден согласиться. Заведите баг в Fernflower.
                                                                                  • 0
                                                                                    А может замена Object на String быть результатом работы оптимизатора?
                                                                                  • –2
                                                                                    js coming
                                                                                    • +3
                                                                                      Вообще ничего общего нет между var в C#/Java и var в JS
                                                                                      • +4
                                                                                        Я поясню, для особо одарённых, которые ставят минусы:

                                                                                        var в JS — это как dynamic в C# — тип определяется в рантайме
                                                                                        var в C# и Java тип определяется при компиляции.

                                                                                        Так что я повторю еще раз: ничего общего нет между var в C#/Java и var в JS
                                                                                        • –5

                                                                                          Это моя любимая тема: на интервью заявляю матёрому джависту, что не вижу разницы между Java и JavaScript и смотрю как он медленно закипает.

                                                                                          • –2
                                                                                            Ну да, это довольно жестоко :)
                                                                                            • +2
                                                                                              Ещё можно сказать, что чего там, программистом работать, знай себе по клавиатуре колоти, зачем такие деньги платить? Ну и сказать, что у вас в компании платят за число строк кода.

                                                                                              Тут не только джависты закипят, думаю.
                                                                                            • –2
                                                                                              Я смотрю глубже.

                                                                                              Лет через пять, в 2023 году, javascript заменит всё и вся.

                                                                                              Насчёт определения типов — определять типы переменных должен компилятор и компьютер, а не человек.
                                                                                              • –1
                                                                                                Всех давно достал этот JS с его нелогичностями, поэтому есть TypeScript и пилят сейчас WebAssembly. Занесло меня недавно случайно на конференцию HolyJS. Там крутые дядьки с умным видом рассказывали про семафоры и хэшкоды. Примерно те же темы обсуждали на конференциях C#/Java лет 10-15 назад. Как будто во что-то мягкое наступил.
                                                                                                • –3
                                                                                                  Вы просто не умеете это готовить

                                                                                                  У меня за 10 лет опыта в разработке на js все нелогичности уже в мозгу сидят и я их спокойно обхожу, хотя в 2003 году да, изрядно помучился, ища странные баги =)
                                                                                          • 0
                                                                                            В общем, можете просто прочитать зачем и как использовать var правильно в любой статье про C# :)
                                                                                            • 0
                                                                                              В целом хорошо, что var появился в Java.
                                                                                              Только теперь и тут будут споры, нужно ли/можно ли его применять.
                                                                                              Применять, видимо, имеет смысл и придется, но аккуратно, находя баланс между краткостью и читаемостью.

                                                                                              Java по своему синтаксису, объектной/функциональной модели, и модели виртуальной машины ближе всего к C#, и поэтому было бы интересно сравнить ситуацию с C#.

                                                                                              И тут возникает интересное.
                                                                                              По одной из версий, в C# var появился в первую очередь для поддержки анонимных типов, экземпляры которых возвращаются LINQ-запросами (а также экземпляры анонимных типов можно конструировать вручную), чтобы можно было обращаться с полям этих типов в статике.

                                                                                              А если var позволяет вывести в статике анонимный тип, то тем более позволяет вывести и известный тип.
                                                                                              Вот только использование анонимных типов в C# вроде так и не стало каким то уж очень массовым, а сам var стал применяться очень часто, к месту и не к месту.

                                                                                              Возникают интересные вопросы.
                                                                                              Что делать в целом с проблемой var. Если в том же C# за много лет и череду новых версий .NET не устоялся единый взгляд на методологию применения var и код пестрит разнобоем, то, видимо, это же ждет и Java.

                                                                                              UPDATE:
                                                                                              Анонимные типы (классы) в Java есть.
                                                                                              Тогда тем более интересно, применим ли к ним var, появившийся в Java.
                                                                                              • 0
                                                                                                По моему это не критично. Лично мне var особо никогда не мешал читать код, хоть я сам его очень редко использую. Другое дело, что обезьяны начнут пихать его куда попало. Правда, обезьяна и без var напишет полную кашу, такую, что var вообще проблемой не покажется.
                                                                                                • +2
                                                                                                  Нет никакой проблемы var. Есть проблема простого кода. Его очень сложно писать и немногие умеют. Легче наворотить абстрактных конструкций, а потом жаловаться на var, который скрывает весь этот ужас, от чего даже сам автор кода не может вспомнить используемые типы.
                                                                                                  • 0
                                                                                                    Действительно интересный момент. Было бы круто написать что-то вроде:
                                                                                                    var foo = new ArrayList<Long>() {
                                                                                                        public int tenSize() {
                                                                                                            return 10 * size();
                                                                                                        }
                                                                                                    };
                                                                                                    С возможностью обратиться потом к методу foo.tenSize(). Я пролистал JEP 286, там написано что переменной var могут быть присвоены значения и анонимного класса, и пересечения типов. Но про доступ к полям и методам там ничего не сказано. Может кто-то в курсе, как с этим обстоят дела?
                                                                                                    • +1
                                                                                                      Отвечаю на свой вопрос: да! Для пересечения типов будут доступны все методы интерфейсов, входящих в пересечение, а для анонимных классов будут доступны все поля и методы, объявленные в них! Получается var не просто синтаксический сахар, с ним мы можем делать то, чего раньше не могли.
                                                                                                      • 0

                                                                                                        Декларируете именованный внутренний класс-наследник ArrayList<Long> и можете делать всё то же самое. Синтаксический сахар в чистом виде.

                                                                                                        • 0
                                                                                                          Нууу, так неинтересно… Хочу именно анонимный класс.
                                                                                                          Ладно, я поторопился, на самом деле можно было вызывать методы через рефлексию:
                                                                                                          Method tenSize = foo.getClass().getMethod("tenSize");
                                                                                                          System.out.println(tenSize.invoke(foo));
                                                                                                          Ктож знал (:
                                                                                                  • +2
                                                                                                    Жаль, что в C#/Java так и не добавили какой-то аналог typedef. Это бы убрало громоздкость при использовании объявлений типа

                                                                                                    InternationalCustomerOrderProcessor<AnonymousCustomer, SimpleOrder<Book>> orderProcessor = createInternationalOrderProcessor(customer, order);


                                                                                                    Вместо этого можно было бы использовать

                                                                                                    MyProcessor orderProcessor = createInternationalOrderProcessor(customer, order);


                                                                                                    Было бы неплохое дополнение к var в тех случаях, когда тип важен при чтении кода и его некак быстро вывести из правой части глазами.
                                                                                                    • +2
                                                                                                      Можно в шапке (в смысле перед namespace) написать

                                                                                                      using OrderProcessor = InternationalCustomerOrderProcessor<AnonymousCustomer, SimpleOrder<Book>>;
                                                                                                      

                                                                                                      как замена typedef. Потом использовать

                                                                                                      OrderProcessor orderProcessor = createInternationalOrderProcessor(customer, order);


                                                                                                      Но. Действует только в одном файле.
                                                                                                      • +2
                                                                                                        Да, именно так и делаю время от времени, но ограничение на один файл печалит.
                                                                                                        • 0
                                                                                                          Да, это бредовая ситуация. Вот кстати, в примере, который я привёл, можно обойтись вполне и var, ибо по названию переменной тип вполне ясен
                                                                                                    • –1
                                                                                                      Я бы предложил для любителей краткости с целью сокращения вот такой записи
                                                                                                      URL codefx = new URL("http://codefx.org");
                                                                                                      

                                                                                                      реализовать вот такой синтаксис
                                                                                                      URL codefx("http://codefx.org");
                                                                                                      

                                                                                                      И избыточность устранена, и новых ключевых слов не надо добавлять.
                                                                                                      А вот в таком коде заменять тип на var — это, на мой взгляд, преступление.
                                                                                                      URLConnection connection = codefx.openConnection();
                                                                                                      

                                                                                                      Тот, кто в этом коде потом будет разбираться, спасибо не скажет.
                                                                                                      var connection = codefx.openConnection();
                                                                                                      
                                                                                                      • –3

                                                                                                        Мне постоянно приходится разбираться с тоннами говнокода на всех языках кроме может быть фортрана и имя типа это последнее, что мне поможет, потому что трудно помнить все эти типа в джаве, шарпе, питоне, objective-c, js и т.п. В случае "openConnection" я просто делаю "go to definition" и смотрю что оно там возвращает. И да, я двумя руками за "var" в джаве — вся эта многословная лапша нисколько не помогает, а вот читать становится сложнее.

                                                                                                      • +2

                                                                                                        Т.е. это эквивалент auto из C++11?