LINUX.ORG.RU

C++ и Java: от кого травма?

 ,


0

2

В Java есть ключевое слово final и в C++ есть похожее ключевое слово const. В Java со всеми объектами работают только через ссылки на них, в C++ это является лишь одной из возможностей. В Java final ссылка на объект означает лишь то, что нельзя менять саму ссылку, но если объект мутабельный, можно менять его состояние. По наивности я думал, что в C++ всё примерно так же, но оказалось, что совсем не так.

Для начала простой пример на Java

public class MainClass {
    private static void foo(final StringBuilder stringBuilder) {
        stringBuilder.append("#foo()");
        System.out.println(stringBuilder);
    }

    public static void main(String[] args) {
        foo(new StringBuilder("Hello from MainClass"));
    }
}

Этот код изменяет содержимое stringBuilder и после этого печатает Hello from MainClass#foo()

Теперь перейдём к коду на C++

#include <iostream>
#include <string>

void foo(std::string& str) {
	str.append("foo()");
	std::cout << str << std::endl;
}


int main() {
	std::string str{"Hello from "};
	foo(str);
	return 0;
}

В этом коде функция foo() так же получает ссылку на стринговый объект, изменяет содержимое этого объекта не меняя саму ссылку и печатает результат: Hello from foo(). Но стоит добавить ключевое слово const перед объявлением этой ссылки в аргументах функции foo() и код перестаёт компилироваться из-за неожиданного отсутствия append().

Среди Java разработчиков попадаются и бывшие сишники. Обычно их можно обнаружить по некоторым привычкам из C/C++. Например по 5 == n, что для Java совершенно бесполезно, поскольку int не кастится в boolean и соответственно ошибочно написанное if (n = 5) приведёт к ошибке компиляции, как и if (5 = n) в C/C++. Так вот в некоторых компаниях Java разработчиков заставляют (например через maven-checkstyle-plugin) объявлять все аргументы публичных методов как final, что совершенно глупо и только раздражает. Сейчас у меня появилось предположение, что это пришло от покусанных C++ бывших сишников.

Так от какого же языка травма?



Последнее исправление: zg (всего исправлений: 2)

скрываю топик

/hide

final specifier для методов java тоже самое что самое что final specifier для методов в C++ - запретить override

final для ссылок в C++ не нужен, потому что их и так можно проинициализировать только один раз, т.е. они иммутабельны, т.е. они по дефолту как final ссылки в жава

зато можно поменять значение указателей, поэтому const *, * const и const * const это три разных иммутабельности.

какие травмы у топикстартера я так и не понял.

peacelove
()
Ответ на: комментарий от peacelove

final для ссылок в C++ не нужен, потому что их и так можно проинициализировать только один раз, т.е. они иммутабельны, т.е. они по дефолту как final ссылки в жава

Да, то есть аналогом кода на Java должен быть вот такой код на C++

#include <iostream>
#include <string>

void foo(const std::reference_wrapper<std::string> str) {
	str.get().append("foo()");
	std::cout << str.get() << std::endl;
}

int main() {
	std::string str{"Hello from "};
	foo(str);
	return 0;
}

Практического смысла от std::reference_wrapper<std::string> тут, разумеется, нет никакого.

zg
() автор топика
Ответ на: комментарий от mono

Хотя мне казалось, что до чехарды с final в java сами додумались.

А чехорда со ссылками (references)? Ведь в C++ их правильнее было бы назвать синонимами (aliases). Ссылка в Java - это именно ссылка, то есть фактически тот же указатель, но без возможности производить с ним адресную арифметику. А в C++ это всего лишь возможно избежать копирование объектов в аргументах функций. Или не только?

zg
() автор топика

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

constexpr explicit operator bool() requires requires {__x.operator bool()} const noexcept(noexcept(__x.operator bool()))
rupert ★★★★★
()
Последнее исправление: rupert (всего исправлений: 2)

Это не травма, это разумная практика, когда все переменные явно делятся на, собственно, переменные переменные и непеременные переменные. Это норма почти во всех современных языках. var/val, let/const, let mut/let.

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

vbr ★★★★
()
Ответ на: комментарий от vbr

В Java в этом нет смысла, поскольку [1] final в аргументе метода делает неизменным не передаваемый объект, а лишь ссылку на него и [2] после компиляции такой final вообще пропадает. final как константа имеет смысл разве что в полях класса. Даже в лямдах компилятор смотрит не на формальную, а на фактическую неизменяемость (effectively final) внешней переменной. Если не говнокодить, методы не разрастаются на сотни или тысячи строк, все переменные (к которым относятся и аргументы) всегда перед глазами и поэтому вводить какую-то защиту от самого себя при помощи final просто глупо. А учитывая и без того многословность и длиннословность кода на Java - ещё и неудобно.

zg
() автор топика
Ответ на: комментарий от zg

Ты пытаешься спорить со всеми современными языками что-ли? В котлине val тоже не делает неизменяемым объект. В JS/TS тоже не делает. И что? Это не делает фичу менее важной.

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

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

А учитывая и без того многословность и длиннословность кода на Java - ещё и неудобно.

Тут соглашусь, поэтому и не использую. Вред от синтаксического мусора перевешивает семантическую пользу.

vbr ★★★★
()

Java - это скриптовый язык для написания умных холодильников, ага Инторнета вещей. А C++ - язык для создания игорей. Как бы языки совсем не пересекаются, и влияние второго на первый лишь опосредованное. Меня больше бесит ехал DAO через DDD сплошь и рядом в PHP, Python, JS - гнойное разлагающее влияние Java на другие языки

rtxtxtrx ★★
()

«В Java final ссылка на объект означает лишь то, что нельзя менять саму ссылку, но если объект мутабельный, можно менять его состояние»

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

Ну и единственное что могу сейчас вспомнить так это то что очень удивило что в Джава не было const а был только final что совсем другое.

Конечно есть вероятность что я не дочитал что-то но очень удивляло как это можно передавать в методы объект по ссылке без возможности защититься в теле метода через сигнатуру передаваемого параметра - ну типа const obect_type &obj - тут не только защита в теле метода от вызова неконстантных методов но и сам интерфейс метода показывал бы его намерения относительно объекта.

bonta ★★★★★
()
Ответ на: комментарий от rtxtxtrx

Если вы не знали подавляющее большинство серверного банковского софта написано на Джава. Подавляющее большинство медицинского софта по управлению всякими томографами и прочим высокоточным медицинским оборудованием написаны на Джава. Потому что надёжно. Потому что при правильном подходе кроме надёжности это еще и быстро в Рантайме. Свидетельство тому ваши смартфоны - там половина IPC между приложений (Интенты) это Джава и работает очень быстро и памяти жрёт не много.

bonta ★★★★★
()
Последнее исправление: bonta (всего исправлений: 1)
Ответ на: комментарий от bonta

Если вы не знали подавляющее большинство серверного банковского софта написано на Джава

Java - это скриптовый язык для написания умных холодильников, ага Инторнета вещей.

серверный банковский софт == Инторнет вещей

Подавляющее большинство медицинского софта по управлению всякими томографами и прочим высокоточным медицинским оборудованием написаны на Джава

ага Инторнета вещей

Потому что надёжно

Так же надежно как 1 + "1" == "11" (когда джоэс за это ругают местные шизики забывают откуда это поведение унаследовано)

Потому что при правильном подходе кроме надёжности это еще и быстро в Рантайме

Она давно не быстрее PHP.

Свидетельство тому ваши смартфоны - там половина IPC между приложений (Интенты) это Джава и работает очень быстро

А половина линукса на баше и питоне крутится. Означает ли это, то что это самые быстрые языки?

памяти жрёт не много

посмеялся

rtxtxtrx ★★
()
Ответ на: комментарий от bonta

Если вы не знали подавляющее большинство серверного банковского софта написано на Джава. Подавляющее большинство медицинского софта по управлению всякими томографами и прочим высокоточным медицинским оборудованием написаны на Джава.

подавляющее большинство

Не удержался - откуда дровишки? И как столь смелое заявление вяжется с

Недавно я пробовал войти в Джава … есть вероятность что я не дочитал что-то …

?

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от vbr

Ты пытаешься спорить со всеми современными языками что-ли? В котлине val тоже не делает неизменяемым объект.

В Котлине изначально продумана изменяемость объектов стандартной библиотеки, например изначально есть изменяемый и неизменяемый List. Правда к самописным объектам это не относится. В этом отношении const в C++ лучше. А все аргументы методов в Котлин являются только и исключительно val, начиная с M5.1 https://blog.jetbrains.com/kotlin/2013/02/kotlin-m5-1/#parameters-are-immutable Мотивация последнего, которую привели в этой статье, звучит какой-то надуманной. Например: «The main reason is that this was confusing: people tend to think that this means passing a parameter by reference, which we do not support (it is costly at runtime)». В Котлине нет примитивных типов, то есть там одни лишь объекты. Как и в Java с объектами в Kotlin работают только через ссылки, а сами ссылки передаются только по значению. Кто на ком стоял и кто кого confusing не понятно, потому что модель памяти в Kotlin ровно та же, что и в Java. Если я что-то упустил в Котлине, буду рад услышать что-то новое.

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

Ну вот в той же статье приводится ещё одна причина: «we all know that mutating parameters is no good style», то есть аргумент в стиле: «общеизвестно что». А какая тут польза или почему именно «no good style» в противном случае рассказать забыли.

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

Нет, это именно говнокод. Тем более в прикладном софте (для системного программирования ни Java ни Kotlin вообще не предназначены). Ну или приведи в пример такой метод на более чем 200 строк, который ты не считаешь говнокодом.

zg
() автор топика
Ответ на: комментарий от rtxtxtrx

Так же надежно как 1 + «1» == «11»

Это всё ерунда. Ты вот это запусти :-)

        System.out.println(File.separatorChar + "abc");
        System.out.println(File.separatorChar + File.pathSeparatorChar + "abc");
zg
() автор топика
Ответ на: комментарий от zg

В Котлине изначально продумана изменяемость объектов стандартной библиотеки

Ничего там не продумано, всё ровно то же, что и в Java, что и не мудрено.

например изначально есть изменяемый и неизменяемый List

В Java библиотек с неизменяемыми коллекциями за 30 лет написали больше, чем HTTP-клиентов, наверное. К языку это отношения не имеет.

А все аргументы методов в Котлин являются только и исключительно val, начиная с M5.1 https://blog.jetbrains.com/kotlin/2013/02/kotlin-m5-1/#parameters-are-immutable Мотивация последнего, которую привели в этой статье, звучит какой-то надуманной. Например: «The main reason is that this was confusing: people tend to think that this means passing a parameter by reference, which we do not support (it is costly at runtime)». В Котлине нет примитивных типов, то есть там одни лишь объекты. Как и в Java с объектами в Kotlin работают только через ссылки, а сами ссылки передаются только по значению. Кто на ком стоял и кто кого confusing не понятно, потому что модель памяти в Kotlin ровно та же, что и в Java. Если я что-то упустил в Котлине, буду рад услышать что-то новое.

Я не думаю, что ты что-то упустил, просто тебе их аргументация кажется надуманной, а мне не кажется. Можешь на JavaScript/TypeScript посмотреть, там тоже есть let и const, работающие ровно так же.

vbr ★★★★
()
Ответ на: комментарий от zg

Ну я же писал уже.

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

К примеру я пишу не просто var x, а ещё и объявляю тип: var x: String. Это даёт тем, кто читает и дорабатывает программу, информацию о том, что переменная x может принимать только строковые значения. Причём компилятору эта информация особо не нужна, всякие выводы типов и так могут всё вывести. Но вот программисты решили, что такие само-ограничения им могут быть полезны. Как для проверяемого компилятором документирования, так и для предотвращения некоторых классов багов.

Возможность объявления неизменяемых переименных (идиотский термин, но не знаю лучшего, «константа» не передаёт всю суть) это ровно из той же области. В подавляющем большинстве исходников 99% переменных не изменяют своего значения. И это привносит смысл в то, чтобы специальным образом обозначать все остальные переменные. Если ты видишь val user = userService.getCurrentUser() ты в уме запоминаешь, что в переменной user лежит текущий юзер и это не изменится никогда. Если ты видишь var user = ..., то ты понимаешь, что эта переменная изменяется 100%, и сразу будешь к ней приглядываться. Любая изменяемая переменная вносит новое измерение сложности в алгоритм. Чем больше переменных меняется, тем сложней алгоритм, число его состояний растёт пропорционально экспоненте числа переменных.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от vbr

Если ты видишь var user = …, то ты понимаешь, что эта переменная изменяется

Если у меня есть структура, которая оборачивает мутабельную ссылку и имеет методы set и get, первый изменяет значение по ссылке, а второй соответственно получает его, то объявляя эту переменную как val, ссылка по прежнему остается мутабельной и метод set изменит значение возвращаемое get-ом. То есть нельзя гарантировать внутреннюю неизменяемость и, например, смело закешировать значение получаемое от get. Правильно?

FishHook
()
Последнее исправление: FishHook (всего исправлений: 1)
Ответ на: комментарий от FishHook

В большинстве языков понятия внутренней иммутабельности нет, поэтому - нельзя. В C++ или Rust можно (но с оговорками, конечно, что все эти иммутабельности построены на доверии).

vbr ★★★★
()
Ответ на: комментарий от vbr

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

Ограничения в полях класса понятны и речь не о них. Но зачем самого себя ограничивать внутри функции в отношении локальных переменных?

В подавляющем большинстве исходников 99% переменных не изменяют своего значения.

Это, мягко говоря, не так.

И это привносит смысл в то, чтобы специальным образом обозначать все остальные переменные. Если ты видишь val user = userService.getCurrentUser() ты в уме запоминаешь, что в переменной user лежит текущий юзер и это не изменится никогда.

Не вижу в этом никакого практического смысла. Если мы говорим о коде какого-то метода или функции и если это не говнокод на несколько сотен строк, то мне и так прекрасно видно что лежит в переменной user и изменяю ли я её после той строки или нет. В аргументах метода/функции, когда меня заставляют объявлять их final, всё ещё хуже. Какой-то корпоративный бюрократ решает за меня как мне пользоваться моими же переменными в написанном мной коде. Иногда эти переменные таки надо изменять и приходится создавать новые. В итоге у меня может быть user из аргументов и ещё какой-то второй локальный user2, допустим инициализируемый именно так, как в твоём примере, если в user из аргументов пришёл null и он final. Ну вот нахрена?

zg
() автор топика
Последнее исправление: zg (всего исправлений: 2)
Ответ на: комментарий от zg

Практический смысл есть. Вон в мире JavaScript тоже принято везде, где это возможно, писать const вместо let. Наверное, не просто так? Ты же работаешь не один, верно? Или пишешь попенсорс и предполагаешь, что будешь работать не один? Всё, конечная. Теперь твой код это не только твоя проблема. Теперь всегда держи это у себя в голове. Иначе будут больно бить.

Недавно разгребал чей-то говнокод. Разработчик почему-то подумал, что дать одной и той же переменной разный смысл в разных контекстах - отличная идея. В начале user значит одно, в середине что-то другое, в конце третье. Получается лапша. Ты сидишь и пальцем водишь по монитору, отслеживая, где и почему меняется семантика переменной. Тратишь время и силы, вместо того, чтобы делать работу. Какая-то нифига не самодокументируемая шляпа. :(

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

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

В этом и смысл.

Конечно, каргокультровать тоже не надо. Где в этом есть настоящий смысл - убирай final и мутируй ссылку. Желательно не меняя её семантики и не размывая её изменения по всему телу функции.

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

witaway
()
Ответ на: комментарий от witaway

Практический смысл есть. Вон в мире JavaScript тоже принято везде, где это возможно, писать const вместо let. Наверное, не просто так?

Если бы ты знал, то рассказал бы. Но ты не знаешь.

Ты же работаешь не один, верно? Или пишешь попенсорс и предполагаешь, что будешь работать не один? Всё, конечная. Теперь твой код это не только твоя проблема. Теперь всегда держи это у себя в голове. Иначе будут больно бить.

На текущем месте работы, где кто-то когда-то решил, что все аргументы должны быть final практически никому больше эти final в аргументах не нужны и при первой же возможности (например в тестах) их не пишут. То есть это просто чьё-то самодурство и не более того. Я работал во многих компаниях и то тут то там появлялись примеры такого самодурства. Например в другой компании зачем-то написали целый фреймворк для юнит тестирования (поверх junit), который проверял абсолютно все методы, включая private, на рандомно кривые параметры. Это приводило к тому, что люди боялись или ленились нормально разделять свой код на части и писали сотни строк подряд только из-за этого. Потому что иначе private метод в 3 - 5 строк превращался в метод на 15 - 20 строк только из-за глупых проверок, спущенных нам сверху.

Недавно разгребал чей-то говнокод. Разработчик почему-то подумал, что дать одной и той же переменной разный смысл в разных контекстах - отличная идея. В начале user значит одно, в середине что-то другое, в конце третье. Получается лапша. Ты сидишь и пальцем водишь по монитору, отслеживая, где и почему меняется семантика переменной. Тратишь время и силы, вместо того, чтобы делать работу. Какая-то нифига не самодокументируемая шляпа. :(

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

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

Без final в аргументе user переменная user2 не нужна. Ты просто присваиваешь user текущего пользователя, если тебе в user передали null. Что может быть проще и понятнее? Иначе придётся копировать user в user2 (можешь дать ему более читабельное название, это не важно), а если пришёл null, то делать какой-то фолбек, как было сказано выше. Далее тебе нужно будет не перепутать эти две переменные в дальнейшем коде. Короче, это глупость и всё из-за final в аргументе user.

Конечно, каргокультровать тоже не надо. Где в этом есть настоящий смысл - убирай final и мутируй ссылку. Желательно не меняя её семантики и не размывая её изменения по всему телу функции.

Ну то есть ты тоже за то, чтобы программист сам решал, без эффективных менеджеров и прочих корпоративных бюрократов, нужен ли ему final тут или там или нет?

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

Ну вот стоило добавить const перед std::string& и у него тут же пропал append(), что и сделало такой стринг неизменным. По-моему это очень хорошая и полезная вещь и очень жаль, что в Java ничего похожего нет. Ошибка компиляции - это гораздо более сильная вещь, чем удержание в голове подробностей той или иной реализации.

zg
() автор топика
Последнее исправление: zg (всего исправлений: 1)
Ответ на: комментарий от zg

Ну то есть ты тоже за то, чтобы программист сам решал, без эффективных менеджеров и прочих корпоративных бюрократов, нужен ли ему final тут или там или нет?

Ну дык. Я же и говорю, жизнь не чёрно-белая. Смысл в этих рекомендациях есть, и, так сложилось, он есть почти всегда. Но ведь правил без исключений не бывает. Всегда надо думать головой и иметь у себя где-то в голове конкретное объяснение, почему ты сделал так, а не иначе. Вариантов, где ссылка не должна быть final миллион. Более чем с вами согласен.

Откуда эти рекомендации вообще взялись? Чтобы дураки не назначали сразу несколько семантик одной и той же переменной.

Почему появился такой карго-культ? Потому что среди менеджеров есть такие же недальновидные товарищи.

Значит ли это, что спецификаторы вроде final не нужны и только сковывают творческую свободу программиста? Ну, вообще, как правило, нет. Просто не нужно возводить в культ.

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

public Result<...> editProfile(final User? requestedUser = null) {
    
    User editingUser = switch(requestedUser) {
        case null   -> userService.getCurrentUser();
        default     -> requestedUser;
    };
    
    ...
}

Или, если знаю, что текущий пользователь тоже будет обязательно где-то использоваться, сделаю вообще так:

public Result<...> editProfile(final User? requestedUser = null) {
    
    User currentUser = userService.getCurrentUser();
    
    User editingUser = switch(requestedUser) {
        case null   -> currentUser;
        default     -> requestedUser;
    };
    
    ...
}

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

Сделать ссылку мутируемой тоже вполне можно, но так, сугубо моё ИМХО, получится на самую каплю менее читаем. Это уже вкусовщина.

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

Но опять же… Это ведь вопрос вкуса и здравого смысла. Всё нормально, что не совсем безобразно.

Ну вот стоило добавить const перед std::string& и у него тут же пропал append(), что и сделало такой стринг неизменным. По-моему это очень хорошая и полезная вещь и очень жаль, что в Java ничего похожего нет. Ошибка компиляции - это гораздо более сильная вещь, чем удержание в голове подробностей той или иной реализации.

Тоже, кстати, не понимаю, как так вышло. Вроде фича маст-хев, но видел её полноценно пока только в плюсах и хрусте. Может ещё где-то есть? В шарпе видел спецификатор readonly, однако, работающий только со структурами.

witaway
()
Последнее исправление: witaway (всего исправлений: 3)
Ответ на: комментарий от bugfixer

1. несколько знакомых - разработчики банковского софта. 2. просто видел софт на котором: 1. работает мрт и кт томографы. 2 работают аппараты лучевой онкологической терапии.

bonta ★★★★★
()
Ответ на: комментарий от zg

Ну вот стоило добавить const перед std::string& и у него тут же пропал append(), что и сделало такой стринг неизменным. По-моему это очень хорошая и полезная вещь и очень жаль, что в Java ничего похожего нет. Ошибка компиляции - это гораздо более сильная вещь, чем удержание в голове подробностей той или иной реализации.

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

anonymous
()
Ответ на: комментарий от bonta
  1. несколько знакомых - разработчики банковского софта. 2. просто видел софт на котором: 1. работает мрт и кт томографы. 2 работают аппараты лучевой онкологической терапии.

Вот то-то и оно. Обмажутся плюсами, потом «слышат».

anonymous
()
Ответ на: комментарий от witaway

Тоже, кстати, не понимаю, как так вышло. Вроде фича маст-хев, но видел её полноценно пока только в плюсах и хрусте. Может ещё где-то есть? В шарпе видел спецификатор readonly, однако, работающий только со структурами.

Не позорься.

anonymous
()
Ответ на: комментарий от witaway
public Result<...> editProfile(final User? requestedUser = null) > {
    
    User editingUser = switch(requestedUser) {
        case null   -> userService.getCurrentUser();
        default     -> requestedUser;
    };
    
    ...
}

Какая религия запрещает тебе написать это вот так:

public Result<...> editProfile(User? requestedUser = null) {
    if (requestedUser == null) {
        requestedUser = userService.getCurrentUser();
    }
    
    ...
}

И короче и понятнее ведь.

zg
() автор топика
Ответ на: комментарий от anonymous

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

А вот и анонимный ламер на связи. Что якобы есть не рассказал, но на вентилятор уже набросил.

zg
() автор топика
Ответ на: комментарий от zg

Какая религия

Никакая.

И короче и понятнее ведь.

Поэтому и говорю: «сугубо моё личное ИМХО», «вопрос вкуса». Ни в коем случае не утверждаю, что это самое правильное лучшее решение.

Я просто ощущаю, что после if-а семантика переменной самую-самую капельку меняется. Это два очень похожих, но разных по смыслу объекта. Они вполне могут иметь два разных имени.

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

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

Пример правда очень упрощенный… Но если придумать что-то более объёмное и неочевидное, падение когнитивной нагрузки становится ощутимым. Опять-таки, это ИМХО.

Если тебе кажется, что будет лучше сделать по-другому — пожалуйста. Твой пример кода тоже вполне адекватен ситуации. Мы ж тут собрались не религиозные войны вести. Я просто предлагаю свой вариант и говорю, что мне в нём нравится. 🤷‍♂️

witaway
()
Последнее исправление: witaway (всего исправлений: 1)
Ответ на: комментарий от witaway

Зато теперь я работаю с editingUser и даже не пытаюсь держать в голове, как он мог меняться и какие у requestedUser были умолчания.

Просто великолепно: была одна сущность, стало две, и ниже по коду нужно не перепутать с которой из них работать.

падение когнитивной нагрузки становится ощутимым.

Наверное со мной совсем что-то не так…

bugfixer ★★★★★
()
Ответ на: комментарий от witaway

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

Скорее всего ты любишь многословный, то есть менее читабельный код. В итоге то, что можно сделать меньшим количеством строк ты делаешь их бОльшим количеством. Если ты не один в проекте, вероятность спутать requestedUser с editingUser ниже по коду увеличилась. А ведь логика кода очень простая - если нам не дали пользователя, мы просто берём его сами из другого места. Нет никакого смысла держать две переменные под это. А представь, что таких объектов несколько или что ты получил varargs таких объектов. Ты будешь весь массив дублировать для этого?

Если тебе кажется, что будет лучше сделать по-другому — пожалуйста. Твой пример кода тоже вполне адекватен ситуации. Мы ж тут собрались не религиозные войны вести. Я просто предлагаю свой вариант и говорю, что мне в нём нравится. 🤷‍♂️

Религиозную войну объявил какой-то самодур (или их несколько) который заставил весь наш R&D в трёх странах использовать maven-checkstyle-plugin с требованием объявлять все аргументы методов как final и валящий билд в противном случае.

zg
() автор топика
Последнее исправление: zg (всего исправлений: 1)
Ответ на: комментарий от zg

Там семантика вся из Модулы-2 и Оберонов взята. Сверху присыпали синтаксисом С++, чтобы свитчеры не пугались.

Кстати, забавно, когда делают очередной безопасный и простой промышленный ЯП, за основу берут языки семейства Модулы в их семантике, но не С и не С++.

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от zg

плюс сборщик мусора

Начиная с Oberon

https://ru.wikipedia.org/wiki/Оберон_(язык_программирования)

виртуальная машина

А это вообще из идеи P-кода и P-машины (было в USCD Pascal)

https://ru.wikipedia.org/wiki/UCSD_Pascal

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

Там семантика вся из Модулы-2 и Оберонов взята. Сверху присыпали синтаксисом С++, чтобы свитчеры не пугались.

Может быть мы с тобой по-разному понимаем что такое семантика ЯП. Расскажи, что ты имеешь в виду и в чём семантика Java ближе к Модуле-2 и к Оберону, нежели к C++.

Начиная с Oberon

Разве сборщик мусора - это чисто обероновская идея? По-моему это просто одна из концепций в Computer Science, никак не связанная с каким-то конкретным ЯП.

А это вообще из идеи P-кода и P-машины (было в USCD Pascal)

У тебя снова путаница. Идея была придумана и реализована гораздо раньше, причём впервые в BCPL (прародитель языка C). В USCD Pascal это просто назвали p-code machine. К появлению Java названия снова поменялись.

https://en.wikipedia.org/wiki/P-code_machine

zg
() автор топика
Последнее исправление: zg (всего исправлений: 1)
Ответ на: комментарий от zg

семантика ЯП

Любой ЯП складывается из трех составляющих: синтаксиса, семантики, прагматики. Синтаксис говорит нам как выглядит та или иная конструкция алгоритма, например условие. Семантика говорит, что эта инструкция означает. например, как трактуется выражение в условном операторе. А прагматика отвечает за то, когда применяется данная конструкция, например, цикл-генератор в Python. Если подробнее пояснять про семантику, то в языках семейства С выражение в условном операторе приводится к целочисленному типу (0, не 0), а в языках семейства Pascal - к логическому (true, false). Точно так же там различается приведение типов, например, в C тебе дадут сделать присвоение long = double, а в Pascal и иже с ним ты должен будешь явно привести тип при присваивании. Точно так же по-разному обстоят дела при создании объектов, где ты можешь вернуть в C или C++ указатель на объект, а в языках семейства Pascal - это ближе к ссылке C++ по своим свойствам. Контроль границ массивом, диапазонов типов и так далее, по этим особенностям можно опознать каким предком это было инспирировано.

LongLiveUbuntu ★★★★★
()
Последнее исправление: LongLiveUbuntu (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

Спасибо за подробный ответ.

Если подробнее пояснять про семантику, то в языках семейства С выражение в условном операторе приводится к целочисленному типу (0, не 0), а в языках семейства Pascal - к логическому (true, false). Точно так же там различается приведение типов, например, в C тебе дадут сделать присвоение long = double, а в Pascal и иже с ним ты должен будешь явно привести тип при присваивании. Точно так же по-разному обстоят дела при создании объектов, где ты можешь вернуть в C или C++ указатель на объект, а в языках семейства Pascal - это ближе к ссылке C++ по своим свойствам. Контроль границ массивом, диапазонов типов и так далее, по этим особенностям можно опознать каким предком это было инспирировано.

В связи с ограничениями тогдашнего железа в языке C пошли на массу компромисов. То есть, например, использование int в качестве ещё и логического типа - это не потому, что так изначально сильно хотели, а потому, что так проще. И логический тип появился вовсе не в Паскале, а задолго до него, например в Алголе. То же самое и с сужающим приведением типов. У математического сопроцессора PDP-11 были инструкции серии STCxx для приведения типов между целыми и нецелыми типами, например STCFI, STCFL, STCDI, STCDL. Последние две буквы: F - float, D - double, I - int, L - long. Подробнее тут на странице 5-16. Не даром C часто называют высокоуровневым ассемблером. Язык C++ вначале появился как надстройка над C и поэтому, из-за необходимости сохранять совместимость с ним, был вынужден продолжать работать так же. В Java требований совместимости с C/C++ не было, поэтому туда принесли вещи, существовавшие раньше, причём задолго до Паскаля. При этом Java - это C подобный язык.

zg
() автор топика