Var и val в Java?

Автор оригинала: Stephen Colebourne
  • Перевод
От переводчика: автор этой заметки — Stephen Colebourne, автор библиотеки Joda Time и Java Time API.


Следует ли добавить вывод типов локальных переменных в Java? Как раз сейчас команда разработчиков языка Java задалась этим вопросом.

Вывод типов локальных переменных


JEP-286 предлагает добавить вывод типов локальных переменных, используя новое псевдоключевое слово (интерпретируемое как «зарезервированное наименование типа»):

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

Предлагается несколько вариантов ключевых слов:

  • var — для изменяемых локальных переменных
  • val — для неизменяемых (final) локальных переменных
  • let — для неизменяемых (final) локальных переменных
  • auto — этот вариант давайте не будем рассматривать, ладно?

Текущая стратегия реализации подразумевает, что ключевое слово final будет всё так же допустимо перед любым из этих вариантов, в результате чего неизменяемые переменные можно будет объявить любым из этих способов:

  • final var — превращает изменяемую локальную переменную в неизменяемую
  • final val — здесь модификатор final избыточен и ничего не меняет
  • final let — здесь модификатор final избыточен и ничего не меняет

Следовательно, по факту выбор сводится к одной из следующих комбинаций:

  • var и final var
  • var и val, но final var и final val допустимы
  • var и let, но final var и final let допустимы

В целом эта возможность не вызывает у меня восторга, и я не уверен, что Java от этого станет лучше. Конечно, при работе в IDE недостаток информации о типах будет сглаживаться. Однако, я полагаю, что это во многих случаях затруднит рецензирование кода, так как оно обычно производится без помощи IDE. Следует также отметить, что соглашение по программированию на C# не рекомендует чрезмерно пользоваться этой возможностью:
Не используйте var, когда тип выражения справа от присваивания не является очевидным.
Не полагайтесь на имя переменной при определении её типа. Оно может не соответствовать истине.

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

Наилучший вариант для Java


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

Главная причина того, что наилучший вариант для Java может быть другим, — это история. В Скале и Котлине эта возможность была с самого начала, а в Java — нет. И я хочу продемонстрировать, почему из-за исторических причин использование val или let не подходит для Java.

Рассмотрим следующий код:
public double parSpread(SwapLeg leg) {
   Currency ccyLeg = leg.getCurrency();
   Money convertedPv = presentValue(swap, ccyLeg);
   double pvbp = legPricer.pvbp(leg, provider);
   return -convertedPv.getAmount() / pvbp;
}

Вывод типов локальных переменных прекрасно бы здесь сработал. Но предположим, что тип переменной в одной из строчек неясен при чтении кода и мы решили указать его явно, следуя рекомендациям C#:
public double parSpread(SwapLeg leg) {
   val ccyLeg = leg.getCurrency();
   Money convertedPv = presentValue(swap, ccyLeg);
   val pvbp = legPricer.pvbp(leg, provider);
   return -convertedPv.getAmount() / pvbp;
}

Всё прекрасно, скажете вы. Но что если этот код пишет команда, где соглашения подразумевают явно помечать локальные переменные словом final?
public double parSpread(final SwapLeg leg) {
   val ccyLeg = leg.getCurrency();
   final Money convertedPv = presentValue(swap, ccyLeg);
   val pvbp = legPricer.pvbp(leg, provider);
   return -convertedPv.getAmount() / pvbp;
}

Внезапно получилась путаница. В одних местах для обозначения неизменяемой переменной используется слово final, а в других местах — слово val. Именно это смешение всё портит и при этом такой проблемы не стоит в Скале и Котлине.

(Возможно, вы не используете final для каждой локальной переменной? Я вот не использую. Но я знаю, что это вполне разумный стандарт, придуманный для улучшения безопасности разработки и уменьшения ошибок.)

А вот альтернативный вариант:
public double parSpread(final SwapLeg leg) {
   final var ccyLeg = leg.getCurrency();
   final Money convertedPv = presentValue(swap, ccyLeg);
   final var pvbp = legPricer.pvbp(leg, provider);
   return -convertedPv.getAmount() / pvbp;
}

Для Java это гораздо более последовательно. Слово final продолжает оставаться повсеместным механизмом для обозначения неизменяемой переменной. А если вы, как и я, не считаете необходимым везде подписывать final, вы просто удаляете его:
public double parSpread(final SwapLeg leg) {
   var ccyLeg = leg.getCurrency();
   Money convertedPv = presentValue(swap, ccyLeg);
   var pvbp = legPricer.pvbp(leg, provider);
   return -convertedPv.getAmount() / pvbp;
}

Я понимаю, какие возражения возникнут у многих читателей в этом месте. Мол, должно быть два новых «ключевых слова», одно для изменяемых и одно для неизменяемых локальных переменных и оба должны быть одинаковой длины (или даже слово для изменяемых должно быть длиннее), чтобы подталкивать людей чаще использовать неизменяемую версию.

Но в Java не всё так просто. Слово final существует многие годы. Если закрыть глаза на этот факт, код превратится в некрасивое и непоследовательное месиво.

Вывод


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

Моя позиция заключается в том, что val и let не подходят для Java, потому что уже существует слово final и оно имеет вполне понятный смысл. Хотя вариант с var и final var не идеален, я считаю, что это единственная из предложенных комбинаций, которая подходит уже существующему языку.
Поделиться публикацией

Комментарии 85

    +8
    По опыту программирования на котлине, самый часто используемый кейз это val. Если для него надо будет писать два слова, все будут использовать везде var, что на мой взгляд еще хуже, чем разброд в использовании final. Думаю, если уж делать, то так, чтобы синтаксис подталкивал к хорошим практикам, а компромисс лучше сместить в сторону final.
      –5
      А зачем объявлять локальные переменные final? компилятор давным-давно научился сам это понимать для оптимизаций, и для использования в лямбдах final уже не нужен, компилятор все сам понимает
      Только фиг нам скорее всего, а не var в Java — новое ключевое слово поломает такое количество кода, что разработчики Java никогда на это не пойдут. С момента запуска языка из новых ключевых слов ввели только strictfp и enum. Как бы не получили мы в итоге default вместо var и final default вместо val :)
        +8
        final нужен чтобы отличать переменные, которые в методе собирались модифицировать от тех, которые не собирались.
        То есть для человека, а не для компилятора (а компилятор нужен чтобы нарушения "правил игры" отловить).
        Но вот как раз для человека — неясно что лучше: короткое, но незнакомое слово (к которому ещё придётся привыкать) или длинное "final var", которое как раз сразу всем будет понятно — но будет длинно, причём уже "навсегда".
          –5
          Ок, круто, но зачем заставлять человека делать то, что легко сделает робот или ide?
          Настройте хук на коммит который будет парсить ast и расставлять файнлы если уж очень хочется, а разработчик найдет куда можно намного более полезно потратить время
            +7
            Ок, круто, но зачем заставлять человека делать то, что легко сделает робот или ide?
            Вы действительно ничего не понимаете или придуриваетесь? Как ваш «робот или ide» поймут — хотел человек делать переменную «g» модифицируемой или нет? Если вы в неё положите 9.8, а какой-нибудь индус ниже по тексту функции — решит засунуть 1.62, то ни один робот и на одна ide не смогут с этим ничего сделать если вы не опишите эту переменную как final!
            а разработчик найдет куда можно намного более полезно потратить время
            Ага. Будет в отладчике сидеть пытаясь выяснить почему у него ракеты не туда летают.
              –2
              Послушайте, мы говорим о внутренних переменных метода, ваш пример немного высосан из пальца. При хорошей архитектуре код метода не превышает экрана, и легко видно, что где меняется.

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

              Кстати, на стаковерфлоу большинство согласны со мной, так что ваша позиция как минимум не бесспорна
              http://stackoverflow.com/questions/316352/why-would-one-mark-local-variables-and-method-parameters-as-final-in-java

              А в вашем примере эта константа никак не должна быть внутренней переменной метода
                +2
                Снова эти плохие люди [зачеркнуто] "индусы" не дают хорошим людям делать правильные вещи. Мда.
                  +1
                  Кстати, на стаковерфлоу большинство согласны со мной, так что ваша позиция как минимум не бесспорна
                  stackoverflow.com/questions/316352/why-would-one-mark-local-variables-and-method-parameters-as-final-in-java
                  У вас двойка в школе по математике или по английскому?
                  +53: You should try to do this, whenever it is appropriate.
                  +27: My personal opinion is that it is a waste of time. I believe that the visual clutter and added verbosity is not worth it.
                  Заметьте что даже те, кто «согласны с вами» соглашаются не с тем, что этого не стоит делать, а с тем, что выигрыш не стоит засорения кода лишними словами. С чем я, пожалуй, тоже соглашусь — но вот как раз тут предложение «var/val» или «var/let» выигрывает: изменяемая и неизменяемая переменные записываются в обоих случаях тремя буквами!
                    –3
                    почитайте объяснение этого ответа (+53)

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

                    ок, вот более свежий пример
                    http://stackoverflow.com/questions/154314/when-should-one-use-final-for-method-parameters-and-local-variables

                    Еще раз проясню свою точку зрения — заставлять людей объявлять все локальные переменные FINAL бессмысленно и затрудняет чтение кода. Если ОЧЕНЬ хочется — можно настроить IDE или коммит-хуки в гите или свн, которые расставят все сами а не заставлять людей делать monkey-job. var/val в этом смысле немногим лучше, но, учитывая историю java, я очень сильно сомневаюсь в том, что введут даже одно новое ключевое слово, не говоря уже о двух сразу.

                      0
                      Чтение кода это как раз улучшает. Это усложняет его написание — но с этим, как бы, никто и не спорит.
                        –2
                        Перечитайте еще раз обсуждение на стековерфлоу: I wish it was the default. But it isn't and I find the code more difficult to understand with finals all over.

                        Вот шикарный баг в эклипсе на эту тему:
                        https://bugs.eclipse.org/bugs/show_bug.cgi?id=130888#c2

                          0
                          Может хватит заниматься самобичеванием?
                          Вот шикарный баг в эклипсе на эту тему:
                          bugs.eclipse.org/bugs/show_bug.cgi?id=130888#c2

                          Баг действительно шикарен: он показывает что за почти десять лет позиция разработчиков Eclipse не изменилась: автоматическое добавление final было добавлено через месяц после появления соответствующего бага, автоматическое удаление… признано нецелесообразным и за 10 лет баг ничуть не изменился (за исключением открытия и закрытия некоторого количества дубликатов).
                          Догадайтесь — как поступили разработчики Eclipse если бы они разделяли вашу точку зрения?
                          Да, есть люди, которые считают, что польза от final не стоит засорения кода лишними словами. Их не так много и я их понимаю. Но большинство — считает что это полезно (по вышеописанным причинам) и ничего вы с этим не сделаете. Да, не у всех хватает силы воли всегда его писать в коде (особенно если вы пишите без IDE), но это другой вопрос.
                          0
                          Очень сильно зависит от привычки. Мой взгляд на таком коде адово спотыкается…

                          Далее по дереву комментариев вы ссылаетесь на опыт Eclipse — во-первых это опыт одной из IDE (сколько у них от рынка Java-разработчиков?), во-вторых и там полно сомнений, а одна из мотивирующих рекомендаций «хватит уже холиваров, оставим как есть». Т.е. имхо это не оправдание данного подхода.

                          А вообще предложенный сахар будет хорошим решением данного холивара (хотя и не закроет его полностью — некоторые лепять файналы и на входные параметры).
                            0
                            Далее по дереву комментариев вы ссылаетесь на опыт Eclipse
                            Это не я ссылаюсь. Это мой оппонент ссылается. В тщетной попытке найти хотя бы одно место в интернете, где бы большинство согласились с его безапелляционным «зачем заставлять человека делать то, что легко сделает робот или ide?». Пока не удалось. Багтрекер Eclipse был просто последним местом, где он пытался это сделать :-)
                              0
                              Послушайте, либо вы тролль, либо не так меня поняли.

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

                              А про роботов а не людей — вот вам пример. Я вот например считаю, что изменять входные параметры функций — жуткий моветон. Но я никогда не заставлю никого объявлять их все файналами, а просто настрою линтер, который не даст закоммитить такой код без соответствующей аннотации «Я ВСЕ ПОНИМАЮ НО НЕ МОГУ ПО ДРУГОМУ»

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

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

                              Еще раз — я далеко не идиот, и свой SCJP сдал еще 15 лет назад. Не спешите писать злобный ответ, а присядьте и подумайте, авось найдете в моих словах что полезное
              +5
              Почитайте JEP. Да и даже из переведённой выше заметки видно, что это не совсем ключевое слово, а «зарезервированное имя типа». Оно имеет специальный смысл только в той позиции, где ожидается тип. Возможен такой код:
              var var = 0;
              val val = var;

              Так что поломает это только если у вас класс называется var или val. Что бывает исключительно редко.
                +1
                Читаю их регулярно и могу сказать одно — если есть любой консервативный метод решить проблему — разработчики коре жава выберут его, даже если решение будет не самым оптимальным. Предлагаемое решение нарушает стройную логику языка — вроде как кейворд но не кейвод.
                Вводить новую механику в язык ради такого кейса — может быть, но верится с трудом
            0
            В том-то и ужас всей жизни, что мы никогда не чувствуем последствий наших поступков. (Эрих Мария Ремарк)
            .
            Нет ничего плохого в введении новых языковых конструкций. Если это облегчит процесс разработки — двумя руками «за». Если добавит новых проблем — «против». При разработке хорошей практикой является определение и следование определенным стилистическим правилам (Java Style Guide, Google Java Style Guide и т.п.) Вопросы начинаются когда они начинают конфликтовать друг с другом.
            .
            Пример Java8 (да простят меня за плохой моветон использования LinkedList):
            .
            List list = new LinkedList();
            станет
            var list = new LinkedList();
            .
            Здесь две проблемы. Во-первых в какой части выражения мы по умолчанию определяем тип generics в правой как раньше, в левой как необходимо при использовании var или в обоих? Во-вторых при неявном определении типа переменной list она будет LInkedList, что позволяет далее использовать ее специфические методы. Разумеется никто не мешает использовать явное определение типа переменной и принудительный cast, но что я должен написать в Style Guide? В каких случаях использование var разрешено, а в каких нет?
            .
            Все это не проблемы если у Вас маленькая команда. Если же часть разработчиков сидит в Индии, часть в Париже, а часть вообще временные консалтеры, то это быстро станет проблемой.
            .
            Хорошая новость. Можно прописать в Style Guide не использовать var.
              +2
              Прошу прошения, из кода были вырезаны generics (спасибо habrahabr)
              List<String> list = new LinkedList<>();
              станет
              var list = new LinkedList<String>();
                +3
                Если вы явно прописываете слева интерфейс/базовый класс, то и var вы бездумно тут не поставите — проблема высосана из пальца.
                  0
                  из кода были вырезаны generics (спасибо habrahabr)
                  Так вот кто, оказывается, против жирнериков выступает! ;D
                  +4
                  Вместо придумывания бессмысленных и глупых примеров лучше просто посмотрите на C#, как он проходил через это. Все пережили и успешно пользуются. Как уже много раз было сказано, var хороший код только улучшит, а плохой плохим и останется.
                  Если добавит новых проблем — «против».

                  Вот вам еще одна мудрость жизненная — все в этом мире имеет плохие и хорошие стороны, реальность она серая. Если вы хотите руководствоваться логикой, что добавлять только белое, то можно вообще в могилу укладываться. В этом мире делать с такой логикой нечего.
                    0
                    при неявном определении типа переменной list она будет LInkedList

                    Совершенно не обязательно. Тип при выводе может определяться всем контекстом использования, а не только контекстом при создании.
                      +5
                      В рамках JEP-286 для вывода типа используется только выражение инициализации, но не дальнейшее использование. Это бы сильно всё усложнило.
                        0
                        А можно пример в котором контекст использования влияет на тип переменной в C#?
                          0
                          Пример
                            class Program
                              {
                                  static void Main(string[] args)
                                  {
                                      Console.WriteLine(Hello("All!"));
                                      Console.WriteLine(Hello("Mary"));
                                      Console.WriteLine(Hello("Jon"));
                          
                                  }
                          
                                  static string Hello(string to)
                                  {
                                      var def = new People();
                          
                                      if (to == "Jon")
                                          def = new Man();
                          
                                      if (to == "Mary")
                                          def = new Women();
                          
                                      Console.WriteLine("Type = {0}", def.GetType());
                                      return def.Hello(to);
                                  }
                          
                              }
                          
                              public class People
                              {
                                  public virtual string Hello(string to)
                                  {
                                      return $"Hello {to}!";
                                  }
                              }
                          
                              public class Man : People
                              {
                                  public override string Hello(string to)
                                  {
                                      return $"Hey {to}!";
                                  }
                              }
                          
                              public class Women : People
                              {
                                  public override string Hello(string to)
                                  {
                                      return $"Hello dear {to}!";
                                  }
                              }

                          Type = ConsoleApplication1.People
                          Hello All!!
                          Type = ConsoleApplication1.Women
                          Hello dear Mary!
                          Type = ConsoleApplication1.Man
                          Hey Jon!

                            0
                            Пример некорректный. Стоит заменить var def = new People(); на какой-нибудь var def = new DefaultPeople(); — и получим ошибку компиляции (Men и Women невозможно привести к типу DefaultPeople)
                              0
                              в c#
                              var def = new People();

                              People def = new People();

                              Одно и тоже. При компиляции var будет заменен на тип.
                              Я так понимаю, в java final var не даст возможность изменить тип на наследуемый.
                                +1
                                Вот именно. В C# первая строчка эквивалентна второй независимо ни от какого "контекста использования", а требовался пример где зависит.
                            +2
                            Такого примера нет — потому что в C# никакой "контекст использования" не влияет на тип переменной. Переменная получает тот тип, который имеет выражение-инициализатор.
                              0
                              Вот и я о том же. В момент инициализации определяется тип. Причем статически во время компиляции. Нет там никакого "контекста использования".
                          +5
                          Не понимаю, а чем плохо, если list будет типом LinkedList? Если эта переменная используется только в своём scope, то совершенно не важно. Если передаётся куда-то, то в этом куда-те будет явно указано List и она станет именно List. Вот совершенно не вижу здесь проблемы.
                            0
                            Проблема в том, что код привязывается к конкретной реализации контейнера. Это может быть необходимо, а может быть и нет. И во втором случае при смене реализации придётся переписывать код. Или отказаться от идеи смены реализации, если переписывание будет слишком затруднительно. При использовании более абстрактного класса этой проблемы не возникает изначально: используем List, пока нам его хватает.

                            Хотя на мой взгляд такая проблема скорее надумана.
                              +1
                              Обычно код, который должен быть независим от конкретного типа, выносится в функцию и в неё передаётся объект этого типа. Я не припомню в своей практике, чтобы в том же scope нужно было бы не зависеть от конкретного типа переменной, объявлённой в этом же scope.
                                0
                                Переписывание будет слишком затруднительно, только если весь код — единственный метод. В коде, разделенном на методы с человечески определенными интерфейсами (там скорее всего и будет максимально абстрактный List) проблем не будет.
                              –1
                              image
                                +3
                                А давайте прикинем, зачем нужен код, который вы боитесь потерять.
                                Зачем вообще можно присваивать ссылку на объект типа MySpecificBlaBla локальной переменной типа BlaBla?
                                Я представляю 3 возможные причины (в порядке убывания разумности):
                                1) Инверсия управления.
                                BlaBla smth = blaBlaFactory.CreateBlaBla(...);
                                В этом случае замена типа на var вообще ничего не изменит. Вывод типов локальный, CreateBlaBla как возвращал базовый класс или интерфейс, так и будет возвращать, так что smth будет того же статического типа (BlaBla).
                                2) Локализация выбора реализации для возможности простого изменения.
                                BlaBla smth = new MySpecificBlaBla();

                                BlaBla smth2 = smth.SomeMethod();
                                В этом случае замена на var так же решает проблему.
                                Да, останется проблема возможности использования методов из MySpecificBlaBla, но если вдруг тип объекта поменяется, IDE или компилятор сразу обратят внимание. Исправить не проблема, если у вас не монструозный метод на 10 экранов.
                                3) Тупо короче писать.
                                тут всё ещё лучше, мало какой базовый тип состоит из 3х символов в нижнем регистре.
                                Ну и ещё одна мысль: в предложенном вами примере, var в java естественнее и безопаснее чем var в C# и auto в C++. В этих языках бывают невиртуальные методы, т.е. замена имени типа локальной переменной на var/auto в 100% рабочем коде может привести к неработоспособности кода, что в java невозможно.
                                Если эти языки пережили внедрение вывода типа и авторы не каются, видимо проблема надуманна.
                                +3
                                А я за var и val, потому что меня напрягает писать ещё 5 лишних символов перед типом. Идея с final var интересна, но остается чувство как будто тебя обманули («как всегда», обещали же литералы для коллекций в 7).
                                  +1
                                  Оказывается не один я такой, кому влом писать final, полностью разделяю данную точку зрения. В случае же с val/var я бы с удовольствием пользовался val.
                                    +3
                                    Все такие. Все понимают, что final везде писать в теории лучше. Но на практике никто не пишет. Поэтому и должен быть простой способ делать переменную final. Лучше бы сделали слово nonfinal, честное слово, полезней было бы для языка.
                                      +1
                                      Увы, задним умом всяк крепок… Имеем что имеем.
                                        0
                                        А изменить — никак? from future import nonfinal :-) или даже from future import final :-)
                                        Меня весьма удивляет эта позиция, когда в язык готовы напихать бог знает чего, а убрать — низя-низя-низя.
                                        Есть же проверенная процедура: в версии N фича добавляется как опциональная, её нужно включать, в версии N+k она включена по умолчанию и включать нужно уже то, что она заменила, в версии N+x её уже нет вообще. k и x подбираются экспериментально, с учётом того, что новые версии Java выходят даже не каждый год k вполне может быть равно единице, x — двум-трём-четырём.
                                        Пока python следовал этому нехитрому правилу — на то, что в нём происходят довольно-таки серьёзные изменения никто не жаловался. Когда они решили сделать финт ушами и всё поломать — поднялся вселенский вой и не факт, что это вообще не закончится расколом языка (a-la perl5 vs perl6).
                                          +1
                                          Нет, нет, у нас по другому. Обратная совместимость это священная корова, никто не вправе её трогать (в том числе менять синтаксис языка)
                                            +3
                                            Код, написанный во времена Java 1.0, компилируется и выполняется сейчас. Код, написанный и скомпилированный тогда, тоже выполняется сейчас. В этом секрет успеха Java. В кровавом энтерпрайсе (с которого Oracle имеет большие деньги) поддержка суперстарого кода очень актуальна.
                                              0
                                              Причем этот старый код часто бывает не покрыт тестами, и часто его даже нельзя покрыть без рефакторинга… А рефакторинг не безопасен. Порочный круг legacy :-)
                                    +2
                                    Как там правильно HolyVal или HolyVar? Всё это сахарок. Вывод типов в параметрах лямбды уже доставляет некоторый дискомфорт. При автокомплите уже выработалась привычка, что IDE подставит нужный тип, но тут привычки приходится менять. А в случае же с var/val, говнокод просто станет изящней, особенно когда вдруг кто-то решит поменять возвращаемый тип функции, результат которой присваивается при помощи var. Например (с потолка) var secureRandom = someMethod(), где возвращаемый тип вместо SecureRandom кто-то просто решит поменять на Random, а название переменной конечно же никто не отрефакторит. И при Code Rewiew это пролетит естественно мимо. Я конечно уверен, что в здравом уме так никто писать не будет, но практика в enterprise доказывает обратное, что если что-то можно написать более коряво, то обязательно кто-то воспользуется данной возможностью.
                                    В общем, резюмируя, в умелых руках сахарок возможно и полезен, но каждый раз, когда захочется написать var/val нужно подумать, а уместно ли здесь это. Или же не думать и просто писать в старом стиле.
                                    Гораздо интереснее, а что там с Project Valhalla a.k.a Value Types?
                                      0
                                      Насчёт Valhalla — движется, но в девятке точно не будет.
                                        0
                                        Да, это будет просто дикий levelup для java платформы. Но почему-то у меня такое чувство, что чем дальше приходит понимание эпичности реализации с тем грузом обратной совместимости, тем более начинает теряться интерес к данному проекту. Или же как с Jigsaw будет.
                                          0
                                          Насколько мне известно, работа идёт весьма активно, просто её реально очень много.
                                        0
                                        Например (с потолка) var secureRandom = someMethod(), где возвращаемый тип вместо SecureRandom кто-то просто решит поменять на Random, а название переменной конечно же никто не отрефакторит.


                                        Идея при переименовании SecureRandom на Random так же переименует эту переменную (или как минимум предложит).
                                          0
                                          Да, было бы здорово, если бы все пользовались идеей и её рефакторингом при смене возвращаемого типа метода.
                                          И вы говорите за какую-то будущую версию идеи? В текущей версии Type Migration не меняет название переменной. А Change Signature не меняет даже тип.
                                            0
                                            Только что проверил в Android Studio (читай, идея) — Shift-F6 по переименованию класса так же предложит переименовать везде переменные.
                                              0
                                              Попробовал, java.security.SecureRandom не переименовывается :\
                                              А если серёзно, то тут же проблема не в переименовании самого типа, в смене возвращаемого методом типа на другой.
                                          0
                                          Разве сложно организовать код так, чтобы принимающая сторона ожидала именно SecureRandom?
                                            0
                                            Я считаю, что несомненно сложно будет организовать "индусов", чтобы всё было по фен-шую.
                                              0
                                              Проблемы индусов (решаемые соглашениями и статическими проверками) не должны быть препятствием на пути в светлое будущее :)
                                                0
                                                Да ведь с "индусов" взятки гладки, их проблемы в итоге мои проблемы, как заказчика. И тогда светлое будущее при таком раскладе — соглашение "Make Type Not Var".
                                            +1
                                            Если вам нужен безопасный рандом, то само название метода должно об этом говорить. В таком случае если кто-то в методе getSuperSEcureRandom меняет реализацию, то код ревью это маловероятно пройдет.
                                              +1
                                              Эх, если бы всегда было бы так просто, то можно было бы вообще тут написать var secureRandom = new SecureRandom(). Но в суровой реальности метод может хоть Ваней называться и быть неподвластной для меня частью, разрабатываемой другим отделом, а вот название локальной переменной при этом может только сбивать с толку.
                                            +1
                                            Лучше let чем val, потому что var и val отличаются всего одной буквой и визуально будут хуже различимы чем var и let.
                                              +2
                                              Но насколько я знаю, именно по этому данная парочка и выбилась в лидеры.
                                                +3
                                                В Scala проблемы с различимостью не замечено
                                                  0
                                                  Думаю, в случае необходимости IDE поможет.
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                    0
                                                    А что такого правильного в иммутабельных значениях? Они нужны далеко не всегда, а во многих случаях просто бессмысленны.
                                                      0
                                                      То, что код получается более строгим, а следовательно, и более безопасным?
                                                      https://habrahabr.ru/post/166113/ "Исключительная красота исходного кода Doom 3"
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                          +1
                                                          Вы так говорите просто потому, что никогда не общались с языками где изменяемые переменные нужно метить :-)
                                                          К хорошему быстро привыкаешь: когда у вас переменные отмечены как неизменяемые, то вы можете быть точно уверены, что в них на протяжении всего метода хранится то, что в них при инициализации положили. Так что не нужно нарезать методы "в лапшу", чтобы в них не запутаться: даже если у вас инциализация незменяемой переменной отеделены сотней строк не вполне тривиального кода вы можете просто весь этот код пропустить (в IDE можно плюсик нажать) и всё.
                                                          При этом на практике как раз оказывается, что изменяемые переменные нужны редко даже если и не прилагать к этому сверхусилий. Однако в Java есть проблема: описать неизменяемую переменную сложнее и дольше, чем изменяемую — потому так редко кто делает.
                                                        +1
                                                        Мне кажется, оптимумом было бы отказаться от синтаксической соли в виде final и добавить синтаксическую соль в виду mutable (как это сделано в Rust, например). Но на это никто не пойдет из-за соображений обратной совместимости. Я думаю, что язык Java так и продолжит свое существование со всеми своими "родовыми травмами" как null'ы, синтаксическая соль для всего immutable, декларацией исключений и т. д. И, в общем и целом, в итоге так или иначе станет маргинальной (не очень скоро, но в обозримом будущем). При этом платформа Java, вероятнее всего, переживет сам язык за счет Скалы, Котлина, Кложура и того, что еще появится.
                                                        На всякий случай — я программист на Java. И, на мой взгляд, в сравнении с прочими jvm-языками она проигрывает. Жалею, что интересоваться этими прочими языками я начал недавно.
                                                          +3
                                                          Как же все любыт писать лишние слова — final, return, имена типов.
                                                          По мне подход Scala лучше. Это легче не только писать, но и читать.
                                                            0
                                                            Скажите это любителям Scalaz)
                                                            0
                                                            Все это странно. Например, в той же scala считается правилом хорошего тона указывать в публичных методах возвращаемый тип. Если будет необходимо с помощью style guide вводить подобное же правило, значит объявление var / val действует только внутри некоторого метода, и единственный вопрос: какое это даст преимущество?
                                                            Опять же, ввиду того, что есть final — val вообще маловероятен (java сама по себе консервативна и дублированием решений ради моды не занимается).
                                                            Очень сомнительный JEP: навеян модными и, возможно, неправильными хотелками.
                                                            На мой взгляд, более фундаментальным решением было бы развитие в сторону макросов, генерализации подхода project
                                                            lombok и т.п. с поддержкой IDE. Это даст возможность вводить изменения подобные данному JEP временно и наблюдать, насколько они приживутся.
                                                              +2
                                                              Annotation processors — вроде вполне официальная штука. А поддержкой IDE разработчики Java не занимаются.
                                                                0
                                                                lombok, кстати умеет val — https://projectlombok.org/features/val.html
                                                                и IDEA 14.1.4 c lombok плагином нормально работает
                                                                +1
                                                                Если даже в допотопный C++ добавили ключевое слово auto, почему бы не сделать то же самое в java?
                                                                  +1
                                                                  Вероятно, вы не в курсе, но слово auto было ключевым в C++ всегда, начиная с самой первой версии.
                                                                    +2
                                                                    Но в те времена оно означало кое-что другое.
                                                                    0
                                                                    Именно потому что C++ — он может быть каким угодно (опасным, сложным, неудобным), но никак не "допотопным" (масса вещей, имеющихся в C++ в Java отсутсвует как класс, хотя есть костыли, отчасти эту недостачу заменяющие… так отсуствие метапрограммирования часто можно заменить импользованием reflection'а).
                                                                    К тому же в C++11 ключевое слово auto — это больше, чем var/val в Java: в C++11 появились объекты, тип которых в принципе невозможно назвать — и тут без auto и declspec будет тяжело. В Java, насколько я знаю, такого [пока?] не предполагается.
                                                                      0
                                                                      Скажу по секрету, уже в Java-8 можно создать переменную (параметр лямбды), тип которой неявно выводится, но явно его задать невозможно. Но я вам этого не говорил ;-)
                                                                        0
                                                                        вы про это?
                                                                        import java.math.BigInteger;
                                                                        
                                                                        public class Main {
                                                                            interface MyCalcLambda { BigInteger run(BigInteger input); }
                                                                        
                                                                            public static void runCalc(MyCalcLambda calc) { }
                                                                        
                                                                            public static void main(String[] args) {
                                                                                runCalc((BigInteger a) -> a.multiply(a));
                                                                            }
                                                                        }
                                                                          0
                                                                          Даже в этом случае, как было показано можно как-то назвать тип переменной куда эту переменную можно положить. В C++11 в точно таком же случае никакого подходящего типа нет вообще (std::function реализована поверх auto/declspec, а не наоборот).
                                                                      0
                                                                      10 лет в яве…
                                                                      Считаю это плохой и весьма бесполезной штукой.
                                                                      Ява всегда была легкой для новичков, начинаем вводить новый класс лупых ошобок для них и делать все немнго сложнее…
                                                                      Зачем портить хороший язык…
                                                                      Напишите новый под ВМ как остальные…
                                                                        +1
                                                                        OFF/2. Я только первый год знакомства называл язык "Ява", общение с грамотными людьми меня в итоге выучило, что правильно всё таки будет "Джава".
                                                                          –3
                                                                          Я очень рад, но мне лично это «по барабану» — можете кофе назвать. Как правильно я знаю…
                                                                          Если вам интересно, у меня немецкий контекст. Я транслитом пишу тут… И унас это буква «j» читается и произносится так как я написал. Надеюсь это никого не оскорбило…
                                                                            +2
                                                                            Ну да, ксерокс, зирокс. Мне тоже действительно по-барабану за ваш кофе, я в том комменте по большей части про себя оффтопил.

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

                                                                      Самое читаемое