От переводчика: автор этой заметки — 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 не идеален, я считаю, что это единственная из предложенных комбинаций, которая подходит уже существующему языку.

Комментарии (84)


  1. guai
    26.03.2016 21:16
    +3

    дико не нравятся варианты
    сокращения :(
    сделали бы let и variable и лень была бы на стороне более правильных иммутабельных значений, variable писать дольше.
    в цейлоне value и variable по этой причине в том числе
    или вообще просто final v = something(); вообще без нового ключевого слова
    для мутабельных можно было бы оставить обязательным класс указывать и тоже лень была бы на стороне добра


    1. NeoCode
      27.03.2016 12:35

      А что такого правильного в иммутабельных значениях? Они нужны далеко не всегда, а во многих случаях просто бессмысленны.


      1. YourLastDoctor
        27.03.2016 12:46

        То, что код получается более строгим, а следовательно, и более безопасным?
        https://habrahabr.ru/post/166113/ "Исключительная красота исходного кода Doom 3"


      1. guai
        27.03.2016 12:51
        +1

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


      1. khim
        27.03.2016 16:10
        +1

        Вы так говорите просто потому, что никогда не общались с языками где изменяемые переменные нужно метить :-)
        К хорошему быстро привыкаешь: когда у вас переменные отмечены как неизменяемые, то вы можете быть точно уверены, что в них на протяжении всего метода хранится то, что в них при инициализации положили. Так что не нужно нарезать методы "в лапшу", чтобы в них не запутаться: даже если у вас инциализация незменяемой переменной отеделены сотней строк не вполне тривиального кода вы можете просто весь этот код пропустить (в IDE можно плюсик нажать) и всё.
        При этом на практике как раз оказывается, что изменяемые переменные нужны редко даже если и не прилагать к этому сверхусилий. Однако в Java есть проблема: описать неизменяемую переменную сложнее и дольше, чем изменяемую — потому так редко кто делает.


  1. Anarchist
    26.03.2016 21:22
    +1

    Мне кажется, оптимумом было бы отказаться от синтаксической соли в виде final и добавить синтаксическую соль в виду mutable (как это сделано в Rust, например). Но на это никто не пойдет из-за соображений обратной совместимости. Я думаю, что язык Java так и продолжит свое существование со всеми своими "родовыми травмами" как null'ы, синтаксическая соль для всего immutable, декларацией исключений и т. д. И, в общем и целом, в итоге так или иначе станет маргинальной (не очень скоро, но в обозримом будущем). При этом платформа Java, вероятнее всего, переживет сам язык за счет Скалы, Котлина, Кложура и того, что еще появится.
    На всякий случай — я программист на Java. И, на мой взгляд, в сравнении с прочими jvm-языками она проигрывает. Жалею, что интересоваться этими прочими языками я начал недавно.


  1. potan
    26.03.2016 21:22
    +3

    Как же все любыт писать лишние слова — final, return, имена типов.
    По мне подход Scala лучше. Это легче не только писать, но и читать.


    1. Sirikid
      26.03.2016 22:58

      Скажите это любителям Scalaz)


  1. autumnl
    27.03.2016 05:54

    Все это странно. Например, в той же scala считается правилом хорошего тона указывать в публичных методах возвращаемый тип. Если будет необходимо с помощью style guide вводить подобное же правило, значит объявление var / val действует только внутри некоторого метода, и единственный вопрос: какое это даст преимущество?
    Опять же, ввиду того, что есть final — val вообще маловероятен (java сама по себе консервативна и дублированием решений ради моды не занимается).
    Очень сомнительный JEP: навеян модными и, возможно, неправильными хотелками.
    На мой взгляд, более фундаментальным решением было бы развитие в сторону макросов, генерализации подхода project
    lombok и т.п. с поддержкой IDE. Это даст возможность вводить изменения подобные данному JEP временно и наблюдать, насколько они приживутся.


    1. lany
      27.03.2016 05:55
      +2

      Annotation processors — вроде вполне официальная штука. А поддержкой IDE разработчики Java не занимаются.


    1. arturgspb
      27.03.2016 16:05

      lombok, кстати умеет val — https://projectlombok.org/features/val.html
      и IDEA 14.1.4 c lombok плагином нормально работает


  1. SerJook
    27.03.2016 10:56
    +1

    Если даже в допотопный C++ добавили ключевое слово auto, почему бы не сделать то же самое в java?


    1. lany
      27.03.2016 10:58
      +1

      Вероятно, вы не в курсе, но слово auto было ключевым в C++ всегда, начиная с самой первой версии.


    1. khim
      27.03.2016 16:14

      Именно потому что C++ — он может быть каким угодно (опасным, сложным, неудобным), но никак не "допотопным" (масса вещей, имеющихся в C++ в Java отсутсвует как класс, хотя есть костыли, отчасти эту недостачу заменяющие… так отсуствие метапрограммирования часто можно заменить импользованием reflection'а).
      К тому же в C++11 ключевое слово auto — это больше, чем var/val в Java: в C++11 появились объекты, тип которых в принципе невозможно назвать — и тут без auto и declspec будет тяжело. В Java, насколько я знаю, такого [пока?] не предполагается.


      1. lany
        27.03.2016 16:43

        Скажу по секрету, уже в Java-8 можно создать переменную (параметр лямбды), тип которой неявно выводится, но явно его задать невозможно. Но я вам этого не говорил ;-)


        1. Borz
          27.03.2016 18:00

          вы про это?

          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));
              }
          }


        1. khim
          27.03.2016 18:20

          Даже в этом случае, как было показано можно как-то назвать тип переменной куда эту переменную можно положить. В C++11 в точно таком же случае никакого подходящего типа нет вообще (std::function реализована поверх auto/declspec, а не наоборот).


  1. shuron
    27.03.2016 15:23

    10 лет в яве…
    Считаю это плохой и весьма бесполезной штукой.
    Ява всегда была легкой для новичков, начинаем вводить новый класс лупых ошобок для них и делать все немнго сложнее…
    Зачем портить хороший язык…
    Напишите новый под ВМ как остальные…


    1. MamOn
      27.03.2016 16:22
      +1

      OFF/2. Я только первый год знакомства называл язык "Ява", общение с грамотными людьми меня в итоге выучило, что правильно всё таки будет "Джава".


      1. shuron
        27.03.2016 16:49
        -3

        Я очень рад, но мне лично это «по барабану» — можете кофе назвать. Как правильно я знаю…
        Если вам интересно, у меня немецкий контекст. Я транслитом пишу тут… И унас это буква «j» читается и произносится так как я написал. Надеюсь это никого не оскорбило…


        1. MamOn
          27.03.2016 17:00
          +2

          Ну да, ксерокс, зирокс. Мне тоже действительно по-барабану за ваш кофе, я в том комменте по большей части про себя оффтопил.