Как стать автором
Обновить

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

ахахаха

Простите братья джависты, вырвалось.
А перегрузку операторов уже завезли?
НЛО прилетело и опубликовало эту надпись здесь
на данный момент в мире c++ в качестве пакетных менеджеров используются разве что vcpkg и conan
к сожалению, перегрузка операторов является очень хорошей абстракцией, если говорить о математике. По этой единственной причине я не считаю Java серьезным языком программирования.
серьезным языком программирования.

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

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

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

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


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

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

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

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

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

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

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

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

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

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

НЛО прилетело и опубликовало эту надпись здесь

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


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

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

Поздно!

Исходя из этой таблицы, в JS (ECMAScript 5/6) всего 50 зарезервированных ключевых слов, которые нельзя применять в качестве переменных.
А здесь представлены ключевые слова в Java. Их тоже 50. Но! в JS количество слов сокращают, а в Java увеличивают.
Но ведь нового ключевого слова не добавили вроде. Вы всё ещё можете писать var var = new Var().
var var = var()
:)
Да, хорошо, что не стали плодить жестких ключевых слов. По большому счёту, ключевые слова — это атавизм. Не могу придумать примера, когда синтаксис языка (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» зарезервировано — тоже не круто.
Сначала лямбды и потоки, теперь это… :'(
Хотя var можно использовать для ухудшения кода
Что? (:
Немного перефразировал это предложение, надеюсь так звучит лучше :)
var может ухудшить читаемость кода, когда присваивается через вызов метода. К примеру код —
var item = GetItem();

не даёт нам точно узнать какой именно тип скрывается под var. Это не страшно при работе в IDE, но при просмотре, к примеру, через браузер в гите уже не узнаешь простым наведением.
Вот честно, я бы ноги отрывал за повсеместное использование 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#, но вас тоже это ждет.
По-моему, все лучше с 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. В документации при том же условии.
Настолько частый паттерн, что я не представляю, как на нем можно спотыкаться при чтении.

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

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

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

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

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

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

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


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

Document[] documents = repo.GetDocuments();

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

Document[] IDocumentRepository.GetDocuments();

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

В 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.

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

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


T foo(T bar) {
return bar;
}

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

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

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

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

Но, к сожалению, приходится идти за модой, чтобы не отставать в конкурентной борьбе. Чтобы новые проекты начинали именно на яве, чтобы в нее инвестировали деньги, нужно привлекать побольше новичков. А новички хотят, чтобы было меньше букв.
Я, как C#-программист, тоже скептически относился к var-ам, когда их добавили. Аргументация была ровно как у Вас. Сейчас настолько привык, что без них уже не представляю жизнь.
Как используете? Лично я только с анонимными типами использую, или в случае длинной составной коллекции, но всё таки редко
Это вопрос вкуса и договорённостей внутри команды. У нас в команде принято использовать 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».

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

Да, как вариант, выглядит неплохо. Но писать дольше :)
У нас в команде принято использовать 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;
}
Это код очень легко читается:
Вызвали метод.
Сохранили данные в переменной.

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

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


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


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


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


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


  • проверка переполнения в проде отключена, в переменной будут неверные данные, они пойдут куда то дальше, еще не так плохо, если где-то дальше приведут к программному исключению до окончания основного алгоритма;
  • а ведь может все без исключений отработать и привести к неверным результатам, когда принимается решение в офлайне на основе полученных от программы данных (а если это медицинская или АСУ ТП система?).
Про 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 класса. Вложенные функции решают эту проблему, но их пока не везде можно использовать.
Полагаю, что суффиксы m, f, d, l, u, ul сделали, т.к. соответствующие типы имеют отражение в регистрах процессора, а про 8- и 16- битные целочисленные типы «знает» только виртуальная машина (CLR в случае .NET) и при трансляции из байт-кода (IL) в машинный код они всегда расширяются до 32-битного типа.

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

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


var result = default(IEnumerable<string>);

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


var number = (long)1;

и


var number = (byte)1;

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


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

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

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

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

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

В принципе, это не такая уж и сложная задача для компилятора — вывести наиболее конкретный обобщённый тип, необходимый в данном контексте. TypeScript успешно с этим справляется. Почему это до сих пор не реализовано в C# — не понятно. Тем более не понятно, почему это не добавили в Java, учитывая, что var ещё только в проекте и можно учесть все минусы существующих языков.
Думаю, вывод наиболее конкретного обобщенного типа не сделали в 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-реализацией интерфейсов и со множественным наследованием интерфейсов.
По-моему, всё гораздо проще. Если после инициализации переменной её не переприсваивают, то можно оставить изначальный тип. А если, скажем, будет такой код:
var names = GetNames(); // GetNames возвращает string[]

if (dropBob)
{
  names = names.Where(name => name != "Bob");
}

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

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


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


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

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

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

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

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

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

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

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

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

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

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


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


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

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

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

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

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

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

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


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

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

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

Компилятор отображает все задействованные типы и помещает их в файлы классов, как если бы вы их вводили сами.
Например, вот результат декомпиляции 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 не может применяться.

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

НЛО прилетело и опубликовало эту надпись здесь
Ваш код компилятор не соберет.

Вас не смущает, что чуть ниже идёт листинг декомпилированного 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"

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

НЛО прилетело и опубликовало эту надпись здесь
А может замена Object на String быть результатом работы оптимизатора?
Вообще ничего общего нет между var в C#/Java и var в JS
Я поясню, для особо одарённых, которые ставят минусы:

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

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

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

Ну да, это довольно жестоко :)
НЛО прилетело и опубликовало эту надпись здесь
Я смотрю глубже.

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

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

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

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

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

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

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

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

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

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

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


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

MyProcessor orderProcessor = createInternationalOrderProcessor(customer, order);


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

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

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

OrderProcessor orderProcessor = createInternationalOrderProcessor(customer, order);


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

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

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

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

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

Ну java это не про краткость.
Я еще не нашел ни одного человека который мог бы мне объяснить чем


parent.getChildren().get(2);

Лучше чем


parent.Children[2];

Очевидно, чем лучше. В джаве вторая конструкция — это дереференс поля и чтение элемента массива. Всегда, в любом контексте, вне зависимости от предыдущих объявлений. Это точно не обращение к базе и не 20 хттп-запросов под капотом. Понимание того, что делает программа, дорого стоит. А в языках с перегрузкой операторов вам придётся изучать контекст и разбираться, что там происходит на самом деле.

Как по мне — это ничего не меняет. Просто в Java будет почти всегда использоваться 2 вызова метода — и вам таки придётся изучать контекст.

Да, в первом случае в Java может быть как «return children[2]», так и 20 http-запросов. Но в том и прелесть, что «извне» интерфейс не изменился. То же самое можно сказать про языки с перегрузкой операторов и полноценными свойствами — под «parent.Children[2]» может ничего не скрываться — и это будет просто чтение из массива, а могут быть те же 20 запросов.
Так и чем в этом случае первая конструкция лучше второй? Разные задачи, разные конструкции. В первом случае нужно декорирование, во втором — нет.
В контексте Java, квадратные скобки применимы только к массивам. А если Children — массив (второй случай), то его реализацию впоследствии никак не поменять. В первом случае getChildren может возвращать любой объект, реализующий интерфейс Collection.

По похожей причине вместо parent.Children нужно использовать функцию getChildren() — ее реализацию впоследствии можно изменить, в отличие от поля Children.

То есть в целом, эта многословность — из-за отсутствия перегрузки операторов и геттеров-сеттеров свойств в Java.
Для тех, кто уже сейчас хочет нормально читать код без прыгающих объявлений и визуального мусора есть неплохой плагин для Intellij Idea — Advanced Code Folding plugins.jetbrains.com/plugin/9320-advanced-java-folding

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

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


var getInstance() { // var не заработает
  ...
}

ClassName getInstance() {
  // никаких ClassName result = new ClassName();
  return ClassName.builder()
    .setter1(value1)
    .setter2(value2)
    .build();
}

var x = new ChildClass(); // x не выведется в базовый тип или интерфейс, посему:

@Autowired
private final var x; // заработает?..

private final var y; // пригодится ли?

@Autowired
public Constructor(ClassName y) {
  this.y = y; // и толку было объявлять его как var?..
}

Естественно, не работает. Было бы глупо, если бы работало. Разве это не очевидно?

НЛО прилетело и опубликовало эту надпись здесь

Любят вот люди потребовать фичу, показав простой пример. Хотя даже тут неочевидно — это int[], long[], double[], Integer[], Number[], Serializable[] или Object[]. Все эти типы подходят. И между ними нет линейного порядка по операции widening как в других случаях с var. А {1, null} — это что? А {1, 1L}? long[] или Number[]? Как насчёт {null, null, null}? Напишите-ка главу спецификации, по которой можно будет однозначно установить тип массива по инициализатору. Желательно, чтобы во всех правилах была какая-то логика.

НЛО прилетело и опубликовало эту надпись здесь
А почему тебе должны платить? Тебе надо фичу, ты и делай:
www.jcp.org/en/jsr/overview
НЛО прилетело и опубликовало эту надпись здесь
Читаемость кода ухудшает невнятная архитектура. Если вы спотыкаетесь о var при чтении, значит в коде много бессмысленных типов-обёрток, а значимых данных мало.
надо сказать, что давно актуально. хотя бы ради for.

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

Поздравляю джавистов. И на вашей улице праздник. Лучше поздно, чем никогда :)

P.S На скале привык к val/var, что не представляю, как эти километровые полотна писать — адская мешанина.
Много приходится рефакторить/переделывать/править существующий (и работающий) код на Java.

Все чаще замечаю, что при правке многострочных stream-операций с лямбдами периодически приходится чесать репу: стоит немного что-то поправить и IDEA радостно подсвечивает все 10 строчек красным — поскольку информации о типах нигде нет и все выводится автоматически, то при потере малейшей части информации вывести тип не представляется возможным, а в явном виде он не указан.

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

Alt+Enter, convert stream to loop :-D

Думаю, что никто не будет обязывать писать объявления через var
Наверное, не ради этого удовольствия выпустили 10-ю версию.
Кстати есть библиотечка, lomblok, там это уже реализовано :)
var вместо типа ссылки. работает также как и описано в статье.

вообще, val как-бы уже есть.
мы используем на проекте, вроде нормально.
НЛО прилетело и опубликовало эту надпись здесь
работаю в IDEA, там все гуд. но вообще нужно плагин для lombock ставить (видимо, поэтому netbeans ругается). попробуйте projectlombok.org/setup/netbeans
stackoverflow.com/questions/36859312/lombok-not-working-in-a-netbeans-project
НЛО прилетело и опубликовало эту надпись здесь
вообщем не понял, при чем здесь ant?
НЛО прилетело и опубликовало эту надпись здесь
Для замыканий (лямбда -выражений) определенно полезно.
Например:
var qdr = x -> x*x;

Но для типизированных переменных делать такое крайне неправильно (ИМХО).
Когда переменная var a;
А что она на самом деле является, крайне не удобно искать.

Вот только, как было отмечено в статье, для лямбда-выражений var не работает. Да и var a; без значения тоже делать не разрешено.
Цитата отсюда:
После выпуска новой редакции Java 8 вместе с lambda-выражениями в Java также было добавлено новое понятие effectively final.

Любая переменная, находящаяся снаружи lambda-выражения и проинициализированная единожды в локальной области видимости(в методе), является effectively final переменной и может быть использована в lambda-выражениях. Effectively final переменная очень похожа на final переменную, с единственным отличием: нет необходимости использовать модификатор final при объявлении переменной.

Теперь ясно. Спасибо.

В языках программирования var нужен не столько для того, чтобы просто укоротить запись, а скорее для ситуаций когда невозможно (на этапе написания кода) определить какого типа будет результирующий объект.
Например, при использовании LINQ на языке C#:
int[] arr = new int[] { 1, 2, 3, 4, 5 };
var result = arr.Select(x => new { x, mod2 = x % 2 }).ToArray();

Так как при проецирование используется анонимный класс, то на момент написания кода нет возможности определить какого типа вернется объект. Это будет массив классов, но имена этим классам будут присвоены лишь на момент компиляции.
Я только учусь Java, и по сути это мой первый языка программирования. Но мне нравится очевидность этого языка. Когда всё расписано(в разумных пределах), а не сокращено до максимума.
Не ужели так много людей пишет в блокноте? Ведь IDE, все проблемы «многословности» легко нивелирует, делая их мнимыми проблемами.

В связи с этим вопрос господа профессионалы, почему вы так не любите очевидность?
Я тоже люблю яву за очевидность, когда можно написать Order order = new Order() и будешь на 100% уверен что order это Order:) И когда вижу код на других языках, где одни кавычки и закорючки, меня это печалит. Но если по работе каждый день приходится просматривать полотна из тысяч строк очевидной явы, глаза и мозг начинают уставать. Появляется желание некоторые очевидные вещи не писать.

Справедливости ради стоит сказать, что в строке var order = new Order() как есть 100%-я уверенность, что order это Order.


А вот в других случаях это м.б. уже неочевидно, отчего и идут дискуссии насчет var.


Кстати, есть случаи, когда явное указание типа как раз может ввести в заблуждение.
Сорри, но я приведу пример из C#.


Вот в таком коде:


foreach (SomeType item in items) // где items - коллекция элементов SomeTypes

item "не совсем" — SomeType.
Дело в том, что если коллекция имеет только нетипизированный (non generic) инумератор, то на каждой итерации foreach мы получаем object, который с помощью объявления SomeType item приводится в рантайме к SomeType.
Это дает как потери производительности, так и в общем случае может привести к InvalidCastException.
В любом случае, когда смотришь на этот код, думаешь одно, а может происходить совсем другое. А казалось бы, куда проще — никакого var — один только SomeType item.


А если написать


foreach (var item in items)

то хотя бы в IDE всплывающая подсказка сразу покажет, items это object или SomeType.


А ведь в C# можно даже такой компилирующийся код написать, дающий исключение в рантайме:


var items = new object[] { null, 0, "123" };
foreach (string s in items)
{
}

Где items — изначально нетипизированная коллекция.
Т.е., явное указание типа в общем случае не 100%-я гарантия.


Такой код более безопасно писать так:


foreach (var s in items.Cast<SomeType>())

здесь сразу "читаемо", что происходит приведение типа (и возможное исключение ожидаемо), и var уместен, т.к. тип уже явно виден.


Или так, если мы хотим пропустить элементы неподходящего типа:


foreach (var s in items.OfType<SomeType>())

Полагаю, что в Java тоже достаточно неоднозначных при чтении моментов (были здесь уже статьи на тему, что то вроде "10 кейсов, знаешь ли ты, как поведет себя Java"), так что сам по себе var — не проблема, тем более, что это и выводом типа не назовешь, скорее, сокращение записи, т.к. тип не выводится, а известен из правой части выражения.

С проблемой в данном примере я абсолютно согласен. В Java есть такая же проблема, но тут уже идёт работа с типами. И никакие var-ы от случайной невнимательности не спасут.

Проблему я вижу именно в том что бы использовать для такого:
var codefx = new URL("http://codefx.org");
var connection = codefx.openConnection();
var reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

В данном примере это часто встречаемая конструкция, и тот кто её уже использовал легко поймут, что тут проходит.
Но а если это будет, что-то более редкое. Что-то, что будет казаться автору понятным и легким, а тому кто поддерживает код (в виду обстоятельств) не очевидным, т.к. он не использовал такой конструкции. А потом ещё на это мы наложим проблему правильного именования переменных.
В итоге мы будем иметь красиво форматированный код, но с не очевидным описанием команд.

Другой пример:
Вот пришёл новичок в мир Java уже после релиза Java 10. Разумеется более быстро находимые статьи будут про новые фичи. Почитав он думает — «Ага, зачем мне писать по старому, по новому меньше печатать». (хотя я опять повторюсь, с IDE проблема надумана)
И вот реализует компонент который использует множественное наследование. И по привычке он пишет:
var вася = new Вася();

вместо,
Человек вася = new Вася();

а потом допустим положил в массив.
Какой будет тип у вася, Человек или Вася?

P.S. пост не нацелен оскорбить Василиев, это просто пример.
P.P.S. Может мне не хватает знаний, рассуждать объективно на эту тему, но мне не нравиться, что в угоду не самой объективной проблеме, придумали двухстороний меч.
P.P.P.S. Если проблема кроется у большинства в:
А что относительно читаемости?
Теперь перейдем к читаемости. Несомненно, когда типы отсутствуют, читаемость должна ухудшиться, не так ли? Вообще говоря, да. Когда вы пытаетесь понять, как работает часть кода, типы являются важным компонентом. И даже если бы 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, и я тут же привык к этому. Это может значительно улучшить читаемость.

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

То почему бы не придумать иное форматирование стиля например, \n\t после объявления типа переменной. Тогда всё имена переменных будут тоже в колонку.
Object
    obj = new Object();
SuperMegaGigaCoolType
    cupCoffee = new SuperMegaGigaCoolType();

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

Заголовок спойлера


Вот были еще добавили перегрузку опереторов. Работа математическими типами стала бы намного удобнее. Есть конечно плагин (https://github.com/amelentev/java-oo), но это все же хак.

Вар позволяет выведение типов, а значит и миксины в анонимных классах… а значит надо поковырять. Статья бредовая и кроме сахарка ничего не описала.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории