Начиная с версии 10 разработчиками было внедрено спорное решение: интегрироваться с экосистемой JS и использоваь ее средства сборки. Если раньше все было достаточно просто и быстро, то сейчас оно тащит node с двухсотмегабайтным бонусом пакетов, и собирается минут 5. Пробовал Vaadin 14, у которого даже официальные сэмплы не собирались. В офисе сборка внезапно подвисает минут на 10, но в лог ничего не пишется — подозреваю, что что-то пытается пробиться сквозь корпоративные прокси. Кроме того, для Vaadin Flow на порядок меньше стало плагинов. Вобщем, все стало как-то тяжко и хрупко. Поэтому продолжаю пользовать Vaadin 8.
P.S. Хоть сам Vaadin 8 и написан на GWT, но практически все аддоны написаны на JS или предоставляют враппер над существующей JS-библиотекой, а посему не требуют никакой дополнительной компиляции при сборке.
Значит jackson сначала создавал класс с помощью конструктора без параметров, а потом вызывал getter/setter.
Ну да, так все делают. Практически всем библиотекам сериализации необходим пустой конструктор (обычно приватный) — только Java Serialization позволено инициировать "пустые" объекты без конструктора, хотя есть способы обхода этого. Для установки данных Jackson может использовть как геттеры-сеттеры, так и прямой доступ к полям любой видимости.
компилятору необходимо передать флаг javac -parameters
Плохой способ. Ни одна из существующих IDE не подцепит это, поэтому придется устанавливать дополнительно в настройках проекта. И вообще код качественно не должет зависеть от флагов компиляции. Если Вы используете Lombok, то есть лучший способ — прописать в lombok.config:
Проще, но с ней вы теряете инкапсуляцию и надёжность графа зависимостей
Люди не вчера зависимости придумали. Maven, Gradle генерят стабильный classpath, плюс позволяют разруливать конфликты. Никаких циклов не может быть — мавен юзает либу наибольшей версии, градл — ту, которая ближе к руту. Для особо дотошных есть dependency tree. Java modules:
привязаны к бинарным артефактам, что делает ваш проект зависимым от способа пакетации ваших классов
не решает проблем изолирования транзитивных зависимостей, как osgi
шатают и вертят всю существовавшую до этого экосистему
ничего реально полезного не привносят, кроме дополнительных ограничений и абстракций
Мне кажется, это еще одна попытка оправдать бесполезность и рудиментарность модульной системы в Java 9. Самый большой ее недостаток — это привязка модулей к бинарным бандлам (jar) и соответвтенно лейауту проекта. Идея classpath проще и универсальней.
К примеру на ФП легче писать, легче читать, легче тестировать и легче понимать
Теоретически. До тех пор, пока не захочется поставить брейкпоинт и тупо задебажить. И вот когда нужно будет дернуть окружение, базу там, файл, сокеты, причем в определенной последовательности — тут начнутся сложности… А вовсе не в хвостовой рекурсии.
Потому, что вам не нужно отслеживать состояние системы. Состояние переменных может не измениться; таким образом, состояние системы остается неизменным.
Не совсем верно. Данные остаются неизменными, зато меняется композиция над этими данными. Что проще: остановить Вселенную и пошагово наблюдать эволюцию состояний во времени, либо докапываться какую функциональную композицию в неявном виде передали ввиде аргумента в ту или иную функцию?
Код как код, что-то преобразуется, что-то делается, но статическому анализатору он не понравился.
Если честно, то и мне этот код не особо нравится :)
Вообще, я иногда сталкиваюсь с подобными случаями, когда анализатор предлагает оптимизировать код в ущерб понятности и читаемости. Он, конечно, на 100% прав, но при последующей ревьюхе человекообразным существом у последнего могут возникнуть вопросы по поводу подобной оптимизации. К сожалению анализатор не предоставляет подробное теоретическое обоснование эквивалентности обоих вариантов, да и ревьюер в них особо вникать не будет — просто попросит: "оставь как раньше — так понятней".
Меня интересует следующий вопрос. Любой софт использует результаты труда третьих лиц, лицензируемых по различным лицензиям, например ввиде библиотек и плагинов. Даже если лицензия открытая и совместимая с коммерческим использованием, заключив эксклюзивный контракт, вы не можете гарантировать эксклюзивные права на владение кодовой базой, так как часть кода не ваша. Более того, даже в вашем коде используются куски из других ваших и чужих проектов, патчей и собственных сборок и копипейсты с туториалов и стековерфлоу, не защищенных никакой лицензией. Вопрос: что в таком случае в контракте является объектом "исходный код" и может ли он являться объектом эксклюзивного владения заказчика?
Как только брокер удостоверится, что сообщение сохранено, он отправит клиенту ответ-подтверждение (4). После чего, поток клиента, первоначально вызвавший операцию send (), может продолжить свою работу.
Во всех подобного рода туториалах реальная проблема замаскирована подобным образом. Очень детально рассмотрено взаимодействие с хардом и дисками, и совершенно не рассмотрен более частый случай сбоя сети: подтверждение может просто не прийти к клиенту, хотя со стороны брокера запись сообщения в хранилище произведена, а подтверждение отправлено. Здесь можно сослаться на сложные архитектурные решения и протоколы ввиде JTA, two-phase commit, XA, etc, но если хорошо подумать, то это только маскирует реальное положение вещей: при помощи асинхронного сетевого TCP/IP невозможно обеспечить полную гарантию "exactly once".
Существует ряд способов получения большей производительности от инфраструктуры брокера
Откажитесь от гарантии (exactly once), тем более, что она не работает. Вместо этого вставьте в консьюмер простую проверку на дубликаты.
По возможности не пересылайте критические данные сообщениями. Вместо этого синхронизируйте реальные данные между системами при помощи сторонних API. Сообщение — это лишь индикатор, что что-то изменилось (возможно), и что данные устарели.
В сценариях, подобному выше, персистенция не нужна, если состояние полностью синхронизируется при старте.
указывает, что сначала был вызван метод premain класса-агента, а затем — метод main исполняемого класса.
Хозяйке на заметку: если не хочется возиться с -javaagent и сложным запуском, то при помощи библиотеки https://github.com/electronicarts/ea-agent-loader можно агента подключать прямо из main(), но когда необходимые классы еще не загружены. Библиотека использует специальный JMX интерфейс OpenJDK.
public interface ReadOnlyCollection extends Iterable
Как я уже писал, Iterable не дает гарантии ReadOnly, т.к. в Iterator-е есть метод remove(). Впринципе на этом можно ставить точку. Нужен ReadOnlyIterable и ReadOnlyIterator.
метод получает UnmodifiableList, сам его менять не может, но знает, что содержимое может быть изменено другой нитью (и умеет это корректно обрабатывать)
Нити здесь ни при чем. UnmodifiableList — это только гарантии того, что консьюмер не может изменить список, то же самое, что и Collections.unmodifiableList(), только на уровне деклараций. Для полностью неизменяемого списка служит именно ImmutableList.
ImmutableList и List не должны быть связаны отношениями наследования. Почему так – разбирается в исходной статье.
Изначально вопрос стоял именно в этом: что будет, если наследовать и что при этом отвалится. Всегда можно нагородить своих библиотек, не связанных напрямую со стандартными коллекциями (это как один из вариантов решения — сделать новый java.collections и добавить конвертеров), но изначальная идея — это исправить уже существующие. И основной кейс использования — это при возврате кастить именно в UnmodifiableCollection:
public class Department {
private List<Person> persons;
/** мы не хотим, чтобы консьюмер изменял список, и въявную это декларируем */
public UnmodifiableList<Person> getPersons() {
// кастится, поскольку List должен наследоваться от UnmodifiableList
// в противном случае пришлось бы врапить new UnmodifiableList(persons)
return persons;
}
/** потому что добавление в список должно быть контролируемым, и только через этот метод. типичный кейс для JPA */
public void addPerson(Person person) {
person.setDepartment(this);
persons.add(person);
}
public void setPersons(UnmodifiableList<Persons> persons) {
// Так вообще нельзя делать, т.к. клиент ничего не должен знать о внутренней реализации списка
// this.persons = persons;
// Так тоже нельзя, потому как может быть persons == this.persons
// this.persons.clear();
this.persons = new ArrayList<>();
persons.stream().forEach(this::addPerson);
}
Остальное — детали реализации, которые впринципе не важны.
А почему автор прицепился именно к ImmutableList, если изменяемые методы есть уже у Collection? Что насчет UnmodifiableSet? Поэтому вместо UnmodifiableList нужен суперинтерфейс UnmodifiableCollection:
interface Collection extends UnmodifiableCollection
> // есть хорошие шансы, что `Iterable`
> // будет достаточно, но давайте предположим, что нам на самом деле нужен список
> public void payAgents(List agents)
Кстати, Iterable, от которой наследуется Collection не является immutable, т.к. возвращаемый Iterator содержит метод remove().
Вобщем, сделать можно, но при этом придется перелопатить всю java util, добавляя Unmodifiable-суперинтерфейсы. Наиболее интересен будет детальный анализ того, что при этом отвалится.
> // лично мне больше нравится возвращать потоки,
> // так как они немодифицируемые, но `List` все равно более распространен
> public List teamRoster() { }
Я видел, как многие так делают, но это ОООчень плохая идея, ибо потоки предназначены для единственного «прогона». При повторном «прогоне» вылезет:
java.lang.IllegalStateException: stream has already been operated upon or closed
> Однако, при далеко идущих изменениях, например, при введении новых коллекций, все не так просто: чтобы такие изменения закрепились, нужно перекомпилировать всю экосистему Java. Это пропащее дело.
Можно например сделать как поступили в Kotlin-е: в байткоде оперировать исключительно старыми добрыми коллекциями, а immutable сделать фичей исключительно компилятора.
> Вы можете себе представить, насколько монументальной и фактически бесконечной была бы такая задача?!
Вот не факт. Как только в Java введут новые коллекции, фреймворки быстро возьмут их на вооружение. Со стримами же как-то разобрались…
У этих методов совершенно разная семантика. Throw это не просто результат функции, а именно раннее завершение всей цепочки вызовов до соответствующего catch-блока. Expected/Try — это урезанная семантика checked exception, в которой нельзя например выкидывать эксепшны разного типа (throws E1, E2). Так делается, потому что во многих функциональных языках нет другой альтернативы. В Java функциональщина всегда будет являться инородным телом и добавит бойлерплейта.
Чесно говоря, не знал о линзах. Насколько я понял, речь о функциональной композиции для манипуляции с иерархическими immutable-структурами. Насчет практического использования не знаю — тут все-равно требуется сторонний кодогенератор типа Immutables, который бы сгенерил withXXX(), плюс генерация самих линз или их задание более удобоваримым способом (как например BeanRef). Как идея интересна. Возможно в BeanRef стоит добавить метод with() для работы с немутабельными объектами, который автоматически реализует "copy-and-set", и транзитивно применяет операцию по всей ветке.
Одна из задач действительно, как уже сказали, байндинг свойств бизнес объектов к интерфейсу. Другая задача может быть, например JPA EntityGraph, где указывается перечень свойств сущности, которые нужно достать из таблицы. И вообще везде, где требуется указание свойств объекта, вместо строки использовать статическую ссылку.
Если структура immutable, поля public final, а ее использование промежуточное, например как результат функции, то вполне нормально. Для DTO тоже потянет (полагаю, что это основной кейс ваших коллег вкупе с каким-нибудь Spring Boot). Для mutable-структуры, хранящей бизнес данные необходимы геттеры-сеттеры, хотя бы потому что много фреймворков не работают с публичными полями.
К кодогенерации типа Immutables.org, JodaBean, AutoBean, QueryDSL etc. не пользуюсь: она хоть и типобезопасная, но затрудняет анализ и рефакторинг кода. Исплючение — ломбок, у него есть хороший плагин для IDEA.
Восходящая звезда среди веб-фреймворков, первая версия для которой появилась всего год назад, в октябре 2018 года, — Micronaut.… Существенное его преимущество — быстрый старт приложений на его основе и малое потребление памяти.
Вот ни разу старт не быстрый. Несмотря на то, что контекст связывается на этапе компиляции, и в рантайме не делается claspath-scan. https://habr.com/ru/company/raiffeisenbank/blog/456376/
Быстрый старт — это Jetty, Javalin, Spark Framework, Vaadin (внезапно!)
У несчастью в линуксе многие комбинации ложатся на комбинации оконного менеджера, особенно, что касается Alt/Ctrl-Fn. В итоге приходится лезть в дебри настроек и менять Alt/Ctrl например на Win.
Видимо потому, что упирается вопрос что есть сознание, и в итоге приводит к идеям вроде солипсизма. Можно строго определить "стороннего наблюдателя" как квантовую или классическую систему, но с определением "наблюдателя-себя" будут проблемы даже в классической механие. Благо, что классика постулирует существование объективного мира, не зависящего от наблюдения.
Поэтому ни одна концепция не разрешает фундаментальные "парадоксы" вроде кота Шредингера, и КД на это тоже не предендует.
Начиная с версии 10 разработчиками было внедрено спорное решение: интегрироваться с экосистемой JS и использоваь ее средства сборки. Если раньше все было достаточно просто и быстро, то сейчас оно тащит node с двухсотмегабайтным бонусом пакетов, и собирается минут 5. Пробовал Vaadin 14, у которого даже официальные сэмплы не собирались. В офисе сборка внезапно подвисает минут на 10, но в лог ничего не пишется — подозреваю, что что-то пытается пробиться сквозь корпоративные прокси. Кроме того, для Vaadin Flow на порядок меньше стало плагинов. Вобщем, все стало как-то тяжко и хрупко. Поэтому продолжаю пользовать Vaadin 8.
P.S. Хоть сам Vaadin 8 и написан на GWT, но практически все аддоны написаны на JS или предоставляют враппер над существующей JS-библиотекой, а посему не требуют никакой дополнительной компиляции при сборке.
Ну да, так все делают. Практически всем библиотекам сериализации необходим пустой конструктор (обычно приватный) — только Java Serialization позволено инициировать "пустые" объекты без конструктора, хотя есть способы обхода этого. Для установки данных Jackson может использовть как геттеры-сеттеры, так и прямой доступ к полям любой видимости.
Плохой способ. Ни одна из существующих IDE не подцепит это, поэтому придется устанавливать дополнительно в настройках проекта. И вообще код качественно не должет зависеть от флагов компиляции. Если Вы используете Lombok, то есть лучший способ — прописать в lombok.config:
Это сгенерит на конструкторе
@java.beans.ConstructorProperties, который Jackson умеет понимать.Нее, пардон. Уровнем выше (автору).
Для особо ленивых как-то так: http://metainf-services.kohsuke.org/
Ну или как альтернатива для рантайма: https://github.com/ronmamo/reflections
Люди не вчера зависимости придумали. Maven, Gradle генерят стабильный classpath, плюс позволяют разруливать конфликты. Никаких циклов не может быть — мавен юзает либу наибольшей версии, градл — ту, которая ближе к руту. Для особо дотошных есть dependency tree. Java modules:
Всю жизнь для подобного использовали:
а для lookup-а сервисов — ServiceLoader.
Мне кажется, это еще одна попытка оправдать бесполезность и рудиментарность модульной системы в Java 9. Самый большой ее недостаток — это привязка модулей к бинарным бандлам (jar) и соответвтенно лейауту проекта. Идея classpath проще и универсальней.
Теоретически. До тех пор, пока не захочется поставить брейкпоинт и тупо задебажить. И вот когда нужно будет дернуть окружение, базу там, файл, сокеты, причем в определенной последовательности — тут начнутся сложности… А вовсе не в хвостовой рекурсии.
Не совсем верно. Данные остаются неизменными, зато меняется композиция над этими данными. Что проще: остановить Вселенную и пошагово наблюдать эволюцию состояний во времени, либо докапываться какую функциональную композицию в неявном виде передали ввиде аргумента в ту или иную функцию?
Если честно, то и мне этот код не особо нравится :)
Вообще, я иногда сталкиваюсь с подобными случаями, когда анализатор предлагает оптимизировать код в ущерб понятности и читаемости. Он, конечно, на 100% прав, но при последующей ревьюхе человекообразным существом у последнего могут возникнуть вопросы по поводу подобной оптимизации. К сожалению анализатор не предоставляет подробное теоретическое обоснование эквивалентности обоих вариантов, да и ревьюер в них особо вникать не будет — просто попросит: "оставь как раньше — так понятней".
Меня интересует следующий вопрос. Любой софт использует результаты труда третьих лиц, лицензируемых по различным лицензиям, например ввиде библиотек и плагинов. Даже если лицензия открытая и совместимая с коммерческим использованием, заключив эксклюзивный контракт, вы не можете гарантировать эксклюзивные права на владение кодовой базой, так как часть кода не ваша. Более того, даже в вашем коде используются куски из других ваших и чужих проектов, патчей и собственных сборок и копипейсты с туториалов и стековерфлоу, не защищенных никакой лицензией. Вопрос: что в таком случае в контракте является объектом "исходный код" и может ли он являться объектом эксклюзивного владения заказчика?
То есть концептуальных вопросов по поводу валидности самой методологии и применяемых методов оценки у вас не возникает?
Во всех подобного рода туториалах реальная проблема замаскирована подобным образом. Очень детально рассмотрено взаимодействие с хардом и дисками, и совершенно не рассмотрен более частый случай сбоя сети: подтверждение может просто не прийти к клиенту, хотя со стороны брокера запись сообщения в хранилище произведена, а подтверждение отправлено. Здесь можно сослаться на сложные архитектурные решения и протоколы ввиде JTA, two-phase commit, XA, etc, но если хорошо подумать, то это только маскирует реальное положение вещей: при помощи асинхронного сетевого TCP/IP невозможно обеспечить полную гарантию "exactly once".
Хозяйке на заметку: если не хочется возиться с -javaagent и сложным запуском, то при помощи библиотеки https://github.com/electronicarts/ea-agent-loader можно агента подключать прямо из main(), но когда необходимые классы еще не загружены. Библиотека использует специальный JMX интерфейс OpenJDK.
А еще можно просто взять Vavr...
Как я уже писал, Iterable не дает гарантии ReadOnly, т.к. в Iterator-е есть метод remove(). Впринципе на этом можно ставить точку. Нужен ReadOnlyIterable и ReadOnlyIterator.
Нити здесь ни при чем. UnmodifiableList — это только гарантии того, что консьюмер не может изменить список, то же самое, что и Collections.unmodifiableList(), только на уровне деклараций. Для полностью неизменяемого списка служит именно ImmutableList.
Изначально вопрос стоял именно в этом: что будет, если наследовать и что при этом отвалится. Всегда можно нагородить своих библиотек, не связанных напрямую со стандартными коллекциями (это как один из вариантов решения — сделать новый java.collections и добавить конвертеров), но изначальная идея — это исправить уже существующие. И основной кейс использования — это при возврате кастить именно в UnmodifiableCollection:
Остальное — детали реализации, которые впринципе не важны.
interface Collection extends UnmodifiableCollection
> // есть хорошие шансы, что `Iterable`
> // будет достаточно, но давайте предположим, что нам на самом деле нужен список
> public void payAgents(List agents)
Кстати, Iterable, от которой наследуется Collection не является immutable, т.к. возвращаемый Iterator содержит метод remove().
Вобщем, сделать можно, но при этом придется перелопатить всю java util, добавляя Unmodifiable-суперинтерфейсы. Наиболее интересен будет детальный анализ того, что при этом отвалится.
> // лично мне больше нравится возвращать потоки,
> // так как они немодифицируемые, но `List` все равно более распространен
> public List teamRoster() { }
Я видел, как многие так делают, но это ОООчень плохая идея, ибо потоки предназначены для единственного «прогона». При повторном «прогоне» вылезет:
java.lang.IllegalStateException: stream has already been operated upon or closed
> Однако, при далеко идущих изменениях, например, при введении новых коллекций, все не так просто: чтобы такие изменения закрепились, нужно перекомпилировать всю экосистему Java. Это пропащее дело.
Можно например сделать как поступили в Kotlin-е: в байткоде оперировать исключительно старыми добрыми коллекциями, а immutable сделать фичей исключительно компилятора.
> Вы можете себе представить, насколько монументальной и фактически бесконечной была бы такая задача?!
Вот не факт. Как только в Java введут новые коллекции, фреймворки быстро возьмут их на вооружение. Со стримами же как-то разобрались…
У этих методов совершенно разная семантика. Throw это не просто результат функции, а именно раннее завершение всей цепочки вызовов до соответствующего catch-блока. Expected/Try — это урезанная семантика checked exception, в которой нельзя например выкидывать эксепшны разного типа (throws E1, E2). Так делается, потому что во многих функциональных языках нет другой альтернативы. В Java функциональщина всегда будет являться инородным телом и добавит бойлерплейта.
Чесно говоря, не знал о линзах. Насколько я понял, речь о функциональной композиции для манипуляции с иерархическими immutable-структурами. Насчет практического использования не знаю — тут все-равно требуется сторонний кодогенератор типа Immutables, который бы сгенерил withXXX(), плюс генерация самих линз или их задание более удобоваримым способом (как например BeanRef). Как идея интересна. Возможно в BeanRef стоит добавить метод with() для работы с немутабельными объектами, который автоматически реализует "copy-and-set", и транзитивно применяет операцию по всей ветке.
Одна из задач действительно, как уже сказали, байндинг свойств бизнес объектов к интерфейсу. Другая задача может быть, например JPA EntityGraph, где указывается перечень свойств сущности, которые нужно достать из таблицы. И вообще везде, где требуется указание свойств объекта, вместо строки использовать статическую ссылку.
Если структура immutable, поля public final, а ее использование промежуточное, например как результат функции, то вполне нормально. Для DTO тоже потянет (полагаю, что это основной кейс ваших коллег вкупе с каким-нибудь Spring Boot). Для mutable-структуры, хранящей бизнес данные необходимы геттеры-сеттеры, хотя бы потому что много фреймворков не работают с публичными полями.
К кодогенерации типа Immutables.org, JodaBean, AutoBean, QueryDSL etc. не пользуюсь: она хоть и типобезопасная, но затрудняет анализ и рефакторинг кода. Исплючение — ломбок, у него есть хороший плагин для IDEA.
Вот ни разу старт не быстрый. Несмотря на то, что контекст связывается на этапе компиляции, и в рантайме не делается claspath-scan. https://habr.com/ru/company/raiffeisenbank/blog/456376/
Быстрый старт — это Jetty, Javalin, Spark Framework, Vaadin (внезапно!)
У несчастью в линуксе многие комбинации ложатся на комбинации оконного менеджера, особенно, что касается Alt/Ctrl-Fn. В итоге приходится лезть в дебри настроек и менять Alt/Ctrl например на Win.
Видимо потому, что упирается вопрос что есть сознание, и в итоге приводит к идеям вроде солипсизма. Можно строго определить "стороннего наблюдателя" как квантовую или классическую систему, но с определением "наблюдателя-себя" будут проблемы даже в классической механие. Благо, что классика постулирует существование объективного мира, не зависящего от наблюдения.
Поэтому ни одна концепция не разрешает фундаментальные "парадоксы" вроде кота Шредингера, и КД на это тоже не предендует.