Я много программировал на Python. Теперь по соображениям работы я пишу код на Java. Проекты, которые я делаю, довольно маленькие, и, возможно, Python будет работать лучше, но есть веские неинженерные причины для использования Java (я не могу вдаваться в подробности).
Синтаксис Java не проблема; это просто другой язык. Но кроме синтаксиса, у Java есть культура, набор методов разработки и практики, которые считаются «правильными». И сейчас я совершенно не в состоянии "проглотить" эту культуру. Поэтому я был бы очень признателен за объяснения или указатели в правильном направлении.
Минимальный полный пример доступен в вопросе переполнения стека, который я начал: https://stackoverflow.com/questions/43619566/returning-a-result-with-several-values-the-java-way/43620339
У меня есть задача - разобрать (из одной строки) и обработать набор из трех значений. В Python это однострочная (кортеж), в Pascal или C 5-строчная запись / структура.
Согласно ответам, эквивалент структуры доступен в синтаксисе Java, а тройка доступна в широко используемой библиотеке Apache - однако «правильный» способ сделать это - создать отдельный класс для значения, дополненный добытчики и сеттеры. Кто-то был очень любезен, чтобы привести полный пример. Это было 47 строк кода (ну, некоторые из этих строк были пустыми).
Я понимаю, что огромное сообщество разработчиков, скорее всего, не "неправильно". Так что это проблема с моим пониманием.
Практики Python оптимизируют для удобочитаемости (что в этой философии приводит к удобству обслуживания), а затем и скорости разработки. С практики оптимизируют для использования ресурсов. Что оптимизируют практики Java? Мое лучшее предположение - масштабируемость (все должно быть в состоянии, готовом для проекта с миллионами LOC), но это очень слабое предположение.
источник
Ответы:
Язык Java
Я считаю, что во всех этих ответах отсутствует смысл, пытаясь приписать намерения тому, как работает Java. Многословие Java не связано с его объектно-ориентированным подходом, поскольку Python и многие другие языки также имеют более жесткий синтаксис. Многословие Java также не связано с поддержкой модификаторов доступа. Это просто то, как Java была разработана и развивалась.
Изначально Java был создан как слегка улучшенный C с ОО. Таким образом, Java имеет синтаксис эпохи 70-х годов. Кроме того, Java очень консервативно относится к добавлению функций, чтобы сохранить обратную совместимость и позволить ей выдержать испытание временем. Если бы в 2005 году Java добавила модные функции, такие как XML-литералы, когда XML был в моде, язык был бы раздут с призрачными функциями, о которых никто не заботится, и которые ограничивали бы его развитие 10 лет спустя. Поэтому в Java просто не хватает современного синтаксиса для краткого выражения концепций.
Тем не менее, нет ничего фундаментального, препятствующего Java принять этот синтаксис. Например, в Java 8 добавлены лямбда-выражения и ссылки на методы, что значительно уменьшает многословие во многих ситуациях. Java также может добавить поддержку для компактных объявлений типов данных, таких как классы case Scala. Но Java просто не сделала этого. Обратите внимание, что пользовательские типы значений находятся на горизонте, и эта функция может ввести новый синтаксис для их объявления. Я полагаю, мы увидим.
Культура Java
История развития корпоративной Java в значительной степени привела нас к культуре, которую мы видим сегодня. В конце 90-х - начале 00-х годов Java стал чрезвычайно популярным языком для серверных бизнес-приложений. В то время эти приложения были в значительной степени написаны специально и включали множество сложных задач, таких как HTTP API, базы данных и обработка XML-каналов.
В 00-х годах стало ясно, что многие из этих приложений имеют много общего, и популярными стали среды для управления этими проблемами, такие как Hibernate ORM, анализатор XML Xerces, JSP и API сервлетов, а также EJB. Однако, несмотря на то, что эти платформы уменьшили усилия для работы в конкретной области, которую они настроили для автоматизации, они требовали конфигурации и координации. В то время, по какой-либо причине, было популярно писать фреймворки для удовлетворения самого сложного варианта использования, и поэтому эти библиотеки были сложны в настройке и интеграции. И со временем они становились все более сложными, поскольку они накапливали особенности. Корпоративное развитие Java постепенно становилось все более и более связано с подключением сторонних библиотек, а не с написанием алгоритмов.
В конце концов, утомительная конфигурация и управление корпоративными инструментами стали достаточно болезненными, и для управления ими пришли фреймворки, прежде всего среда Spring. Согласно теории, вы можете поместить всю свою конфигурацию в одном месте, и инструмент конфигурирования затем сконфигурирует части и соединит их вместе. К сожалению, эти «каркасные рамки» добавили больше абстракции и сложности поверх всего шара воска.
За последние несколько лет популярность библиотек возросла. Тем не менее, целое поколение Java-программистов достигло совершеннолетия во время развития тяжелых корпоративных сред. Их ролевые модели, разработчики фреймворков, писали о фабричных фабриках и загрузчиках bean-компонентов конфигурации прокси. Им приходилось ежедневно настраивать и интегрировать этих чудовищ. И в результате культура сообщества в целом последовала примеру этих рамок и имела тенденцию к чрезмерной чрезмерной инженерии.
источник
for
цикла, когда amap
будет более подходящим (и читаемым). Есть счастливая среда.Я считаю, что у меня есть ответ на один из вопросов, которые вы подняли, которые не были подняты другими.
Java оптимизирует обнаружение ошибок программиста во время компиляции, никогда не делая никаких предположений
В общем, Java имеет тенденцию выводить факты об исходном коде только после того, как программист уже явно выразил свое намерение. Компилятор Java никогда не делает никаких предположений о коде и будет использовать только вывод для сокращения избыточного кода.
Причиной этой философии является то, что программист только человек. То, что мы пишем, не всегда то, что мы на самом деле намереваемся сделать программой. Язык Java пытается смягчить некоторые из этих проблем, вынуждая разработчика всегда явно объявлять их типы. Это просто способ двойной проверки того, что написанный код действительно выполняет то, что предполагалось.
Некоторые другие языки продвигают эту логику еще больше, проверяя предварительные условия, постусловия и инварианты (хотя я не уверен, что они делают это во время компиляции). Для программиста это еще более экстремальный способ заставить компилятор перепроверить свою работу.
В вашем случае это означает, что для того, чтобы компилятор гарантировал, что вы действительно возвращаете типы, которые, по вашему мнению, возвращаете, вам необходимо предоставить эту информацию компилятору.
В Java есть два способа сделать это:
Используйте в
Triplet<A, B, C>
качестве возвращаемого типа (который на самом деле должен бытьjava.util
, и я не могу объяснить , почему это не так . Тем более JDK8 ввестиFunction
,BiFunction
,Consumer
,BiConsumer
и т.д. ... Это только кажется , чтоPair
и поTriplet
крайней мере , будет иметь смысл. Но я отвлекся )Для этого создайте свой собственный тип значения, в котором каждое поле будет правильно названо и введено.
В обоих этих случаях компилятор может гарантировать, что ваша функция возвращает объявленные ею типы и что вызывающая программа понимает, какой тип каждого возвращаемого поля, и использует их соответствующим образом.
Некоторые языки обеспечивают статическую проверку типов и вывод типов одновременно, но это оставляет дверь открытой для тонкого класса проблем несоответствия типов. Где разработчик намеревался вернуть значение определенного типа, но фактически возвращает другое, и компилятор STILL принимает код, потому что случается так, что по совпадению и функция, и вызывающая сторона используют только методы, которые могут применяться как к намеченному и фактические типы.
Рассмотрим что-то подобное в Typescript (или типе потока), где вместо явной типизации используется вывод типа.
Конечно, это глупый тривиальный случай, но он может быть намного более тонким и приводить к трудностям в отладке, потому что код выглядит правильно. Такого рода проблемы полностью избегаются в Java, и именно на этом и основан весь язык.
Обратите внимание, что в соответствии с надлежащими объектно-ориентированными принципами и доменным моделированием, случай разбора продолжительности в связанном вопросе также может быть возвращен как
java.time.Duration
объект, который был бы намного более явным, чем оба вышеупомянутых случая.источник
Java и Python - два языка, которые я использую больше всего, но я иду с другой стороны. То есть я был глубоко в мире Java, прежде чем начал использовать Python, поэтому я мог бы помочь. Я думаю, что ответ на более широкий вопрос «почему все так тяжело» сводится к двум вещам:
yield
.Java «оптимизирует» программное обеспечение с высокой стоимостью, которое будет поддерживаться в течение многих лет большими группами людей. У меня был опыт написания материала на Python и год спустя, и я был озадачен моим собственным кодом. В Java я могу посмотреть на крошечные фрагменты кода других людей и сразу же узнать, что он делает. В Python вы не можете этого сделать. Дело не в том, что один лучше, как вы, кажется, понимаете, просто у них разные затраты.
В конкретном случае, который вы упоминаете, нет кортежей. Простое решение - создать класс с общедоступными ценностями. Когда появилась Java, люди делали это довольно регулярно. Первая проблема заключается в том, что это головная боль при обслуживании. Если вам нужно добавить некоторую логику или безопасность потоков или вы хотите использовать полиморфизм, вам, по крайней мере, нужно будет коснуться каждого класса, который взаимодействует с этим объектом 'tuple-esque'. В Python есть решения для этого, такие как
__getattr__
и т.д., так что это не так страшно.Есть некоторые вредные привычки (ИМО) вокруг этого, хотя. В этом случае, если вам нужен кортеж, я спрашиваю, почему вы сделали его изменчивым объектом. Вам нужны только геттеры (на заметку, я ненавижу соглашение get / set, но оно таково). Я думаю, что пустой класс (изменяемый или нет) может быть полезен в частном или частном контексте пакета в Java , То есть, ограничивая ссылки в проекте на класс, вы можете позже выполнить рефакторинг по мере необходимости, не изменяя открытый интерфейс класса. Вот пример того, как вы можете создать простой неизменный объект:
Это своего рода шаблон, который я использую. Если вы не используете IDE, вы должны начать. Он сгенерирует геттеры (и сеттеры, если они вам нужны), так что это не так больно.
Я чувствовал бы упущением , если бы я не указывал на то , что уже есть тип , который казалось бы , чтобы удовлетворить большинство ваших потребностей здесь . Если оставить в стороне, используемый вами подход не очень подходит для Java, так как он играет на своих слабостях, а не на своих сильных сторонах. Вот простое улучшение:
источник
Прежде чем вы слишком разозлитесь на Java, прочитайте мой ответ в другом посте .
Одной из ваших жалоб является необходимость создания класса, чтобы просто возвращать некоторый набор значений в качестве ответа. Это действительное беспокойство, которое, я думаю, показывает, что ваши интуиции программирования верны! Тем не менее, я думаю, что другие ответы не достигают цели, придерживаясь примитивного анти-паттерна одержимости, которому вы привержены. И Java не обладает такой же простотой работы с несколькими примитивами, как Python, где вы можете возвращать несколько значений изначально и легко присваивать их нескольким переменным.
Но как только вы начинаете думать о том, что
ApproximateDuration
тип делает для вас, вы понимаете, что он не ограничен настолько узко, что «просто, казалось бы, ненужный класс, чтобы вернуть три значения». Концепция, представленная этим классом, на самом деле является одной из ваших основных бизнес-концепций в области доменных имен - необходимость уметь представлять приблизительное время и сравнивать его. Это должно быть частью вездесущего языка ядра вашего приложения, с хорошей поддержкой объектов и доменов, чтобы оно могло быть протестировано, модульно, повторно использовано и полезно.Является ли ваш код, который суммирует приблизительную длительность вместе (или длительности с допустимым пределом погрешности, как бы вы это ни указывали), полностью процедурным или есть какая-то объектность для него? Я бы предположил, что хороший дизайн, заключающийся в суммировании приблизительных длительностей, будет диктовать выполнение этого вне любого потребляющего кода, в пределах класса, который сам может быть протестирован. Я думаю, что использование такого типа доменного объекта будет иметь положительный волновой эффект в вашем коде, который поможет вам отойти от построчных процедурных шагов для выполнения одной высокоуровневой задачи (хотя и с множеством обязанностей), к классам с одной ответственностью которые свободны от конфликтов разных интересов.
Например, предположим, что вы узнаете больше о том, какая точность или масштаб на самом деле требуются для правильного суммирования и сравнения продолжительности, и вы обнаружите, что вам нужен промежуточный флаг для обозначения «ошибки приблизительно 32 миллисекунд» (близко к квадрату корень из 1000, поэтому на полпути логарифмически между 1 и 1000). Если вы привязали себя к коду, который использует примитивы для представления этого, вам придется найти каждое место в коде, где у вас есть,
is_in_seconds,is_under_1ms
и изменить его наis_in_seconds,is_about_32_ms,is_under_1ms
, Все должно было бы измениться повсюду! Создание класса, в обязанности которого входит запись допустимой погрешности, чтобы его можно было использовать в другом месте, освобождает ваших потребителей от знания деталей о том, какие значения имеют значения погрешности или что-либо о том, как они сочетаются, и позволяет им просто указывать допустимый предел погрешности. сейчас. (То есть ни один потребительский код с допустимым пределом погрешности не будет принудительно изменен при добавлении в класс нового запаса погрешности, поскольку все старые допустимые пределы погрешности все еще действительны).Заключительное заявление
Жалобы на тяжесть Java, похоже, уходят, когда вы приближаетесь к принципам SOLID и GRASP и более продвинутой разработке программного обеспечения.
добавление
Я добавлю совершенно беспристрастно и несправедливо, что автоматические свойства C # и возможность назначать свойства «только для получения» в конструкторах помогают еще больше убрать немного грязный код, который потребуется «пути Java» (с явными частными полями поддержки и функциями получения / установки) :
Вот реализация Java выше:
Теперь это чертовски чисто. Обратите внимание на очень важное и преднамеренное использование неизменности - это кажется решающим для этого особого вида ценностного класса.
В этом отношении этот класс также является достойным кандидатом на
struct
роль типа значения. Некоторое тестирование покажет, имеет ли переключение на структуру выигрыш в производительности во время выполнения (может).источник
И Python, и Java оптимизированы для удобства обслуживания в соответствии с философией их дизайнеров, но у них совершенно разные представления о том, как этого добиться.
Python - это мультипарадигмальный язык, который оптимизирует для ясности и простоты кода (легко читать и писать).
Java (традиционно) - это ОО-язык, основанный на классах с одной парадигмой, который оптимизирует для ясности и согласованности - даже за счет более подробного кода.
Кортеж Python - это структура данных с фиксированным числом полей. Та же функциональность может быть достигнута обычным классом с явно объявленными полями. В Python естественно предоставлять кортежи в качестве альтернативы классам, потому что это позволяет значительно упростить код, особенно благодаря встроенной поддержке синтаксиса для кортежей.
Но это не совсем соответствует культуре Java для предоставления таких ярлыков, поскольку вы уже можете использовать явные объявленные классы. Не нужно вводить другой тип структуры данных просто для того, чтобы сохранить некоторые строки кода и избежать некоторых объявлений.
Java предпочитает единую концепцию (классы), последовательно применяемую с минимумом синтаксического сахара особого случая, в то время как Python предоставляет несколько инструментов и множество синтаксических сахаров, чтобы вы могли выбрать наиболее удобный для любой конкретной цели.
источник
Не ищите практики; как правило, это плохая идея, как сказано в передовой практике ПЛОХО, шаблоны ХОРОШИЕ? , Я знаю, что вы не спрашиваете о лучших практиках, но я все же думаю, что вы найдете там некоторые важные элементы.
Поиск решения вашей проблемы лучше, чем практика, и ваша проблема не в том, чтобы быстро вернуть три значения в Java:
Здесь у вас есть неизменяемый объект, содержащий только ваши данные, и общедоступный, поэтому нет необходимости в геттерах. Однако обратите внимание, что если вы используете некоторые инструменты сериализации или персистентный слой, например ORM, они обычно используют getter / setter (и могут принять параметр для использования полей вместо getter / setter). И именно поэтому эти практики используются очень часто. Поэтому, если вы хотите узнать о практиках, лучше понять, почему они здесь, чтобы лучше их использовать.
Наконец: я использую геттеры, потому что я использую много инструментов сериализации, но я их тоже не пишу; Я использую ломбок: я использую ярлыки, предоставленные моей IDE.
источник
Об идиомах Java в целом:
Существуют различные причины, по которым в Java есть классы для всего. Насколько мне известно, главная причина:
Ява должна быть легкой в освоении для начинающих. Чем более явные вещи, тем труднее пропустить важные детали. Меньше волшебства случается, что будет трудно понять новичкам.
Что касается вашего конкретного примера: аргументация для отдельного класса такова: если эти три вещи достаточно сильно связаны друг с другом, чтобы они возвращались как одно значение, стоит назвать эту «вещь». А введение имени для группы вещей, которые структурированы обычным способом, означает определение класса.
Вы можете уменьшить шаблон с помощью таких инструментов, как Lombok:
источник
Есть много вещей, которые можно сказать о культуре Java, но я думаю, что в случае, если вы столкнулись с этим прямо сейчас, есть несколько важных аспектов:
"Структурировать" классы с полями
Как уже упоминалось в других ответах, вы можете просто использовать класс с открытыми полями. Если вы сделаете их окончательными, вы получите неизменный класс и инициализируете их с помощью конструктора:
Конечно, это означает, что вы привязаны к определенному классу, и все, что когда-либо нужно для получения или использования результата анализа, должно использовать этот класс. Для некоторых приложений это нормально. Для других это может вызвать некоторую боль. Большая часть Java-кода посвящена определению контрактов, что обычно приводит вас к интерфейсам.
Еще одна ловушка заключается в том, что при подходе на основе классов вы открываете поля, и все эти поля должны иметь значения. Например, isSeconds и millis всегда должны иметь какое-то значение, даже если isLessThanOneMilli имеет значение true. Какой должна быть интерпретация значения поля миллис, если isLessThanOneMilli имеет значение true?
«Структуры» как интерфейсы
С помощью статических методов, разрешенных в интерфейсах, на самом деле относительно легко создавать неизменяемые типы без большого количества синтаксических издержек. Например, я мог бы реализовать такую структуру результатов, о которой вы говорите, примерно так:
Это все еще много шаблонного, я абсолютно согласен, но есть и несколько преимуществ, и я думаю, что они начинают отвечать на некоторые из ваших основных вопросов.
С такой структурой, как этот результат анализа, контракт вашего синтаксического анализатора очень четко определен. В Python один кортеж на самом деле не отличается от другого кортежа. В Java доступна статическая типизация, поэтому мы уже исключаем определенные классы ошибок. Например, если вы возвращаете кортеж в Python и хотите вернуть кортеж (millis, isSeconds, isLessThanOneMilli), вы можете случайно сделать:
когда вы имели в виду:
С таким интерфейсом Java вы не можете скомпилировать:
вообще. Ты должен сделать:
Это преимущество статически типизированных языков в целом.
Этот подход также дает вам возможность ограничить, какие ценности вы можете получить. Например, при вызове getMillis () вы можете проверить, является ли isLessThanOneMilli () истиной, и, если это так, сгенерировать исключение IllegalStateException (например), поскольку в этом случае нет значащего значения millis.
Трудно сделать неправильную вещь
В приведенном выше примере интерфейса у вас все еще есть проблема, заключающаяся в том, что вы можете случайно поменять местами аргументы isSeconds и isLessThanOneMilli, поскольку они имеют одинаковый тип.
На практике вы действительно можете использовать TimeUnit и длительность, чтобы у вас был такой результат:
Это будет намного больше кода, но вам нужно написать его только один раз, и (при условии, что вы правильно документировали вещи), люди, которые в конечном итоге используют ваш код, не должны догадываться, что означает результат, и не может случайно сделать такие вещи, как
result[0]
когда они имеют в видуresult[1]
. Вы по-прежнему можете довольно кратко создавать экземпляры, и извлекать из них данные не так уж и сложно:Обратите внимание, что вы могли бы сделать что-то подобное и с подходом на основе классов. Просто укажите конструкторы для разных случаев. Тем не менее, у вас все еще есть вопрос, для чего инициализировать другие поля, и вы не можете запретить доступ к ним.
В другом ответе говорилось, что корпоративная природа Java означает, что большую часть времени вы создаете другие библиотеки, которые уже существуют, или пишете библиотеки для использования другими людьми. Ваш общедоступный API не должен требовать много времени для просмотра документации, чтобы расшифровать типы результатов, если этого можно избежать.
Вы пишете эти структуры только один раз, но создаете их много раз, так что вы все еще хотите это краткое создание (которое вы получаете). Статическая типизация гарантирует, что данные, которые вы получаете от них, соответствуют вашим ожиданиям.
Теперь, несмотря на это, есть места, где простые кортежи или списки могут иметь большой смысл. При возврате массива чего-либо может быть меньше накладных расходов, и если это так (и это накладные расходы значительны, что вы определите с помощью профилирования), то использование простого массива значений внутри может иметь большой смысл. Ваш публичный API, вероятно, все еще должен иметь четко определенные типы.
источник
originalTimeUnit=DurationTimeUnit.UNDER1MS
помощью вызывающего, попытался прочитать значение в миллисекундах.Проблема в том, что вы сравниваете яблоки с апельсинами . Вы спросили, как имитировать возврат более чем одного значения, приведя пример быстрого и грязного Python с нетипизированным кортежем, и вы фактически получили практически однострочный ответ .
Принятый ответ дает правильное бизнес-решение. Нет быстрого временного обходного пути, который вы должны были бы отбросить и правильно реализовать в первый раз, когда вам нужно было бы сделать что-нибудь практичное с возвращаемым значением, но класс POJO, который совместим с большим набором библиотек, включая персистентность, сериализацию / десериализацию, контрольно-измерительные приборы и все возможное.
Это тоже совсем не долго. Единственное, что вам нужно написать, - это определения полей. Сеттеры, геттеры, hashCode и равно могут быть сгенерированы. Таким образом, ваш реальный вопрос должен заключаться в том, почему геттеры и сеттеры не генерируются автоматически, но это проблема синтаксиса (синтаксическая проблема сахара, некоторые скажут), а не проблема культуры.
И, наконец, вы задумываетесь над тем, чтобы ускорить то, что совсем не важно. Время, потраченное на написание классов DTO, незначительно по сравнению с временем, затрачиваемым на обслуживание и отладку системы. Поэтому никто не оптимизирует для меньшего многословия.
источник
Есть три разных фактора, которые способствуют тому, что вы наблюдаете.
Кортежи против именованных полей
Пожалуй, самый тривиальный - в других языках вы использовали кортеж. Спорить о том, является ли кортежи хорошей идеей, на самом деле не главное, но в Java вы использовали более тяжелую структуру, поэтому это немного несправедливое сравнение: вы могли бы использовать массив объектов и некоторое приведение типов.
Синтаксис языка
Может быть проще объявить класс? Я не говорю о том, чтобы сделать поля открытыми или использовать карту, а о классах дел в Scala, которые предоставляют все преимущества описанной вами установки, но гораздо более кратки:
Мы могли бы иметь это - но есть цена: синтаксис становится более сложным. Конечно, это может стоить того в некоторых случаях, или даже в большинстве случаев, или даже в большинстве случаев в течение следующих 5 лет - но об этом нужно судить. Кстати, это одна из приятных вещей с языками, которые вы можете изменить самостоятельно (например, lisp) - и обратите внимание, как это становится возможным благодаря простоте синтаксиса. Даже если вы на самом деле не изменяете язык, простой синтаксис позволяет использовать более мощные инструменты; например, я часто пропускаю некоторые опции рефакторинга, доступные для Java, но не для Scala.
Философия языка
Но самым важным фактором является то, что язык должен обеспечивать определенный образ мышления. Иногда это может показаться угнетающим (я часто хотел поддержки определенной функции), но удаление функций так же важно, как и их наличие. Не могли бы вы поддержать все? Конечно, но тогда вы могли бы просто написать компилятор, который компилирует каждый язык. Другими словами, у вас не будет языка - у вас будет расширенный набор языков, и каждый проект в основном будет иметь подмножество.
Конечно, можно написать код, который противоречит философии языка, и, как вы заметили, результаты часто бывают ужасными. Наличие класса с несколькими полями в Java сродни использованию var в Scala, вырождению предикатов пролога для функций, выполнению unsafePerformIO в haskell и т. Д. Java-классы не предназначены для облегчения - они не предназначены для передачи данных , Когда что-то кажется трудным, часто полезно отступить назад и посмотреть, есть ли другой путь. В вашем примере:
Почему длительность отделена от единиц? Существует множество временных библиотек, которые позволяют вам объявить продолжительность - что-то вроде Duration (5, секунд) (синтаксис будет различаться), что позволит вам делать все, что вы хотите, гораздо более надежным способом. Может быть, вы хотите преобразовать его - зачем проверять, если результат [1] (или [2]?) Равен «часу» и умножается на 3600? И третий аргумент - какова его цель? Я предполагаю, что в какой-то момент вам придется распечатать «менее 1 мс» или фактическое время - это логика, которая естественно связана с данными времени. Т.е. у вас должен быть такой класс:
}
или что вы действительно хотите сделать с данными, следовательно, инкапсулируя логику.
Конечно, может быть случай, когда этот способ не сработает - я не говорю, что это магический алгоритм для преобразования результатов кортежей в идиоматический код Java! И могут быть случаи, когда это очень уродливо и плохо, и, возможно, вам следовало бы использовать другой язык - вот почему их так много!
Но мой взгляд на то, почему классы являются «тяжелыми структурами» в Java, заключается в том, что вы не должны использовать их в качестве контейнеров данных, а в качестве отдельных ячеек логики.
источник
Насколько я понимаю, основные причины
Это дает вам «Вы должны написать класс для возврата для любого нетривиального результата», что, в свою очередь, довольно тяжело. Если вы используете класс вместо интерфейса, вы можете просто иметь поля и использовать их напрямую, но это привязывает вас к конкретной реализации.
источник
def f(...): (Int, Int)
, это функция,f
которая возвращает значение, которое оказывается кортежем целых чисел). Я не уверен, что дженерики в Java - проблема с этим; обратите внимание, что Haskell также выполняет стирание типа, например. Я не думаю, что есть техническая причина, по которой у Java нет кортежей.Я согласен с ответом JacquesB, что
Но четкость и последовательность не являются конечными целями для оптимизации. Когда вы говорите «Python оптимизирован для удобства чтения», вы сразу же упоминаете, что конечной целью является «ремонтопригодность» и «скорость разработки».
Чего вы добиваетесь, когда у вас есть четкость и согласованность, выполненные Java-способом? Я считаю, что он эволюционировал как язык, который претендует на обеспечение предсказуемого, последовательного, единообразного способа решения любой проблемы программного обеспечения.
Другими словами, культура Java оптимизирована для того, чтобы заставить менеджеров поверить, что они понимают разработку программного обеспечения.
Или, как сказал один мудрец очень давно ,
источник
(Этот ответ не является объяснением для Java в частности, но вместо этого обращается к общему вопросу «Что может оптимизировать [тяжелая] практика?»)
Рассмотрим эти два принципа:
Попытка оптимизировать одну из этих целей может иногда мешать другой (то есть усложнение неправильных действий может также усложнять правильные действия или наоборот).
Выбор компромисса в каждом конкретном случае зависит от приложения, решений программистов или команды, о которых идет речь, и культуры (организации или языкового сообщества).
Например, если ошибка или перерыв в работе вашей программы на несколько часов может привести к гибели людей (медицинские системы, аэронавтика) или даже просто к потере денег (например, миллионы долларов, скажем, в рекламных системах Google), вы сделаете разные компромиссы (не только на вашем языке, но также и в других аспектах инженерной культуры), чем для одноразового сценария: он, скорее всего, склоняется к «тяжелой» стороне.
Другие примеры, которые делают вашу систему более «тяжелой»:
Это всего лишь несколько примеров, чтобы дать вам представление о случаях, когда создание «тяжелых» вещей (и затруднение для быстрого написания некоторого кода) может быть действительно преднамеренным. (Можно даже утверждать, что если написание кода требует больших усилий, это может привести к более тщательному размышлению перед написанием кода! Конечно, эта строка аргумента быстро становится нелепой.)
Пример: внутренняя система Python от Google имеет тенденцию делать вещи «тяжелыми», так что вы не можете просто импортировать чужой код, вы должны объявить зависимость в файле BUILD , команде, код которой вы хотите импортировать, нужно объявить свою библиотеку как виден ваш код и т. д.
Примечание : все вышесказанное относится к случаю, когда вещи становятся «тяжелыми». Я абсолютно не утверждаю, что Java или Python (либо сами языки, либо их культура) являются оптимальным компромиссом для любого конкретного случая; это тебе думать. Две связанные ссылки на такие компромиссы:
источник
Со временем культура Java развивалась под сильным влиянием как программного обеспечения с открытым исходным кодом, так и корпоративного программного обеспечения - что является странным сочетанием, если вы действительно об этом думаете. Корпоративные решения требуют тяжелых инструментов, а открытый исходный код требует простоты. Конечным результатом является то, что Java находится где-то посередине.
Часть того, что влияет на рекомендацию, - это то, что считается читаемым и поддерживаемым в Python, а Java сильно отличается.
Я упоминаю только C #, потому что в стандартной библиотеке есть набор классов Tuple <A, B, C, .. n>, и это прекрасный пример того, насколько громоздкими являются кортежи, если язык не поддерживает их напрямую. Почти в каждом случае ваш код становится более читабельным и обслуживаемым, если вы правильно выбрали классы для решения этой проблемы. В конкретном примере в вашем связанном вопросе переполнения стека другие значения будут легко выражены как вычисленные геттеры возвращаемого объекта.
Интересное решение, которое разработала платформа C #, которая обеспечивает «золотую середину», - это идея анонимных объектов (выпущенных в C # 3.0 ), которые хорошо справляются с этим зудом. К сожалению, у Java пока нет эквивалента.
До тех пор, пока языковые функции Java не будут изменены, наиболее удобочитаемым и поддерживаемым решением является наличие выделенного объекта. Это связано с ограничениями в языке, которые восходят к его появлению в 1995 году. Первоначальные авторы планировали гораздо больше языковых возможностей, которые никогда не делали этого, и обратная совместимость является одним из основных ограничений, окружающих эволюцию Java с течением времени.
источник
Я думаю, что одним из основных аспектов использования класса в этом случае является то, что то, что происходит вместе, должно оставаться вместе.
У меня было это обсуждение с другой стороны, об аргументах метода: рассмотрим простой метод, который вычисляет ИМТ:
В этом случае я бы поспорил против этого стиля, потому что вес и рост связаны между собой. Метод «сообщает», что это два отдельных значения, когда это не так. Когда бы вы рассчитали ИМТ с весом одного человека и ростом другого? Это не имеет смысла.
Имеет больше смысла, потому что теперь вы четко сообщаете, что рост и вес происходят из одного источника.
То же самое касается возврата нескольких значений. Если они четко связаны, верните небольшой аккуратный пакет и используйте объект, если они не возвращают несколько значений.
источник
Честно говоря, культура заключается в том, что программисты на Java изначально имели тенденцию выходить из университетов, где преподавали объектно-ориентированные принципы и принципы устойчивого проектирования программного обеспечения.
Как ErikE говорит в своем слове больше слов, вы, похоже, не пишете устойчивый код. Из вашего примера я вижу, что есть очень неловкое запутывание проблем.
В культуре Java вы, как правило, будете знать, какие библиотеки доступны, и это позволит вам достичь гораздо большего, чем просто программирование. Таким образом, вы бы обменяли свои особенности на шаблоны проектирования и стили, которые были опробованы и испытаны в жестких промышленных условиях.
Но, как вы говорите, это не без недостатков: сегодня, используя Java более 10 лет, я склонен использовать либо Node / Javascript, либо Go для новых проектов, потому что оба позволяют ускорить разработку, а с архитектурой в стиле микросервиса это часто достаточно. Судя по тому факту, что Google сначала активно использовал Java, но был создателем Go, я думаю, они могли делать то же самое. Но даже сейчас, когда я использую Go и Javascript, я все еще использую многие навыки проектирования, которые я приобрел за годы использования и понимания Java.
источник