Please open Telegram to view this post
VIEW IN TELEGRAM
😁18❤1😴1
microJUG
Виктор Кланг предложил добавить новый метод gather() в стримы. Это что-то вроде collect(), но ещё более общая операция, через которую можно будет реализовать все остальные. Эдакие коллекторы 2.0. Это наконец-то сделает стримы расширяемыми. #stream
Предложение в итоге вылилось в JEP 461: Stream Gatherers (Preview)
👍11🤔3💩2✍1🔥1
Вышел Spring 6.1 GA 🥳
Вместе с ним выходит Spring Boot 3.2, но это произойдёт 23 ноября, так что надо чуть-чуть подождать.
В релизе Spring 6.1 тонна всего нового, но главная фишка – это, конечно же, поддержка виртуальных потоков. Включаются они флагом
Ещё из интересного:
Новый интерфейс RestClient. Он синхронный, как и старый RestTemplate, но имеет современное удобное fluent API, похожее на WebClient, появившийся в Spring 5. Он не использует WebFlux, а значит не требует зависимости spring-webflux. Т.к. теперь есть виртуальные потоки, то синхронность уже не является проблемой и не вредит масштабируемости. RestClient, как и WebClient, разумеется, поддерживает декларативные HTTP-клиенты, созданные из интерфейсов с аннотациями @HttpExchange.
Новый интерфейс JdbcClient. Это современная альтернатива JdbcTemplate с fluent API:
Ещё появилась поддержка CRaC, поддержка
#spring
Вместе с ним выходит Spring Boot 3.2, но это произойдёт 23 ноября, так что надо чуть-чуть подождать.
В релизе Spring 6.1 тонна всего нового, но главная фишка – это, конечно же, поддержка виртуальных потоков. Включаются они флагом
spring.threads.virtual.enabled=true (требуется JDK 21+). С виртуальными потоками можно спокойно вызывать блокирующее API и ни о чём не беспокоиться.Ещё из интересного:
Новый интерфейс RestClient. Он синхронный, как и старый RestTemplate, но имеет современное удобное fluent API, похожее на WebClient, появившийся в Spring 5. Он не использует WebFlux, а значит не требует зависимости spring-webflux. Т.к. теперь есть виртуальные потоки, то синхронность уже не является проблемой и не вредит масштабируемости. RestClient, как и WebClient, разумеется, поддерживает декларативные HTTP-клиенты, созданные из интерфейсов с аннотациями @HttpExchange.
Новый интерфейс JdbcClient. Это современная альтернатива JdbcTemplate с fluent API:
Optional<Integer> value = client.sql("SELECT AGE FROM CUSTOMER WHERE ID = :id")
.param("id", 3)
.query(Integer.class)
.optional();Ещё появилась поддержка CRaC, поддержка
@HttpExchange аннотаций в MVC-контроллерах и многое другое. Подробный список можно посмотреть тут.#spring
❤18🎉9🥱1
Что там по Java 22? Список JEP'ов на текущий момент такой (и я думаю, окончательный):
JEP 456: Unnamed Variables & Patterns
JEP 459: String Templates (Second Preview)
JEP 447: Statements before super(...) (Preview)
JEP 455: Primitive types in Patterns, instanceof, and switch (Preview)
JEP 463: Implicitly Declared Classes and Instance Main Methods (Second Preview)
JEP 458: Launch Multi-File Source-Code Programs
JEP 457: Class-File API (Preview)
JEP 461: Stream Gatherers (Preview)
JEP 454: Foreign Function & Memory API
JEP 460: Vector API (Seventh Incubator)
JEP 462: Structured Concurrency (Second Preview)
JEP 423: Region Pinning for G1
JEP 464: Scoped Values (Second Preview)
Список довольно внушительный, но если отбросить превью/инкубаторы, то остаётся только Unnamed Variables & Patterns, Foreign Function & Memory API, Launch Multi-File Source-Code Programs и Region Pinning for G1. В принципе, безымянные переменные/паттерны – неплохая фича, я бы попробовал.
А что вы думаете? Будете переходить на Java 22 с 21, когда выйдет? 😆 Ну а если серьёзно, то всё это станет стабильным в следующем LTS (Java 25), и это будет действительно мощный релиз (может, там даже и Valhalla будет).
#java22
JEP 456: Unnamed Variables & Patterns
JEP 459: String Templates (Second Preview)
JEP 447: Statements before super(...) (Preview)
JEP 455: Primitive types in Patterns, instanceof, and switch (Preview)
JEP 463: Implicitly Declared Classes and Instance Main Methods (Second Preview)
JEP 458: Launch Multi-File Source-Code Programs
JEP 457: Class-File API (Preview)
JEP 461: Stream Gatherers (Preview)
JEP 454: Foreign Function & Memory API
JEP 460: Vector API (Seventh Incubator)
JEP 462: Structured Concurrency (Second Preview)
JEP 423: Region Pinning for G1
JEP 464: Scoped Values (Second Preview)
Список довольно внушительный, но если отбросить превью/инкубаторы, то остаётся только Unnamed Variables & Patterns, Foreign Function & Memory API, Launch Multi-File Source-Code Programs и Region Pinning for G1. В принципе, безымянные переменные/паттерны – неплохая фича, я бы попробовал.
А что вы думаете? Будете переходить на Java 22 с 21, когда выйдет? 😆 Ну а если серьёзно, то всё это станет стабильным в следующем LTS (Java 25), и это будет действительно мощный релиз (может, там даже и Valhalla будет).
#java22
✍8👍3🤯1
👍16🎉4💩1
JetBrains запустил JetBrains AI. Это AI ассистент в IDE, который умеет многое, например, может объяснить код, предложить рефакторинг, предложить мультистроковое автодополнение, сконвертировать код в другой язык, сгенерировать коммит-сообщение, документацию, юнит-тесты и т.д. и т.п. Всё это он может, потому что глубоко анализирует окружающий контекст (текущий файл, проект, зависимости, язык...)
Также есть AI чат с ассистентом.
Ассистент работает через JetBrains AI Service, который в свою очередь использует OpenAI.
Подписка, разумеется, платная: 100$ в год или 10$ в месяц, если помесячно. Есть free trial. При этом сама IDE должна быть с лицензией (IDEA Community не подойдёт).
Чтобы включить ассистента, нужно обновиться до IDEA 2023.3.
#idea
Также есть AI чат с ассистентом.
Ассистент работает через JetBrains AI Service, который в свою очередь использует OpenAI.
Подписка, разумеется, платная: 100$ в год или 10$ в месяц, если помесячно. Есть free trial. При этом сама IDE должна быть с лицензией (IDEA Community не подойдёт).
Чтобы включить ассистента, нужно обновиться до IDEA 2023.3.
#idea
💩20👍11
Брайн Гетц предложил идею расширить возможности оператора
Примеры:
#switch
switch ещё больше, добавив в него возможность обрабатывать исключения в ветках case. Напомню, что с Java 21 switch поддерживает паттерны, записи и case null. По его мнению этого недостаточно, т.к. с помощью них можно обработать только успешные результаты, но для ошибок приходится прибегать к классическому оператору try-catch. Было бы классно, если бы исключения можно было обрабатывать в той же манере, что и успешные результаты, т.е. прямо в ветках switch.Примеры:
String allLines = switch (Files.readAllLines(path)) {
case List<String> lines -> lines.stream().collect(Collectors.joining("\n"));
case throws IOException _ -> "";
}var opt = switch (Integer.parseInt(s)) {
case int i -> Optional.of(i);
case throws NumberFormatException _ -> Optional.empty();
};#switch
👍37🔥12🤔4⚡1
Сравнение производительности сборщиков мусора в Java 8, 17 и 21 (throughput, latency, pause times, memory overhead).
TLDR:
• Java 8 значительно хуже почти по всем параметрам, чем 17 и 21. Срочно апгрейдимся, если ещё на 8.
• 21 лучше 17 не настолько сильно, но разница тоже ощутима.
• G1 самый экономный сборщик в плане памяти из трёх.
• ZGC кушает много, но это трейдофф, благодаря которому возможны паузы < 1ms.
• Generational ZGC лучше, чем legacy ZGC. Так что обязательно включаем ключ -XX:+ZGenerational (если вы на 21 и у вас ZGC). В будущем legacy ZGC исчезнет полностью.
TLDR:
• Java 8 значительно хуже почти по всем параметрам, чем 17 и 21. Срочно апгрейдимся, если ещё на 8.
• 21 лучше 17 не настолько сильно, но разница тоже ощутима.
• G1 самый экономный сборщик в плане памяти из трёх.
• ZGC кушает много, но это трейдофф, благодаря которому возможны паузы < 1ms.
• Generational ZGC лучше, чем legacy ZGC. Так что обязательно включаем ключ -XX:+ZGenerational (если вы на 21 и у вас ZGC). В будущем legacy ZGC исчезнет полностью.
👍12✍10🔥7
Давайте поговорим про валидацию входных аргументов. На первый взгляд тема кажется совсем банальной, но есть в ней несколько нюансов, которым, на мой взгляд, уделяют недостаточно внимания.
Есть, к примеру, следующая запись:
Чего здесь не хватает? Правильно, проверок на
Как-то слишком длинно. Если у нас в проекте сотни подобных проверок (все ж пишут проверки, так ведь?🙂), то код сильно раздувается. Хочется покомпактнее. Вспоминаем, что в Java 8 появился метод Objects.requireNonNull(). Заменяем:
Гораздо лучше. Но теперь вспоминаем, что
Есть там и другие проверки: checkNotNull(), checkElementIndex(), checkPositionIndex(), checkState(). При этом checkArgument() из них самый универсальный, и с его помощью можно проверить любое boolean выражение:
В итоге мы смогли уложиться в две строчки, что очень хорошо: проверки не должны занимать много места.
В Гуаве при этом решено ещё несколько проблем.
Представим, что нам ещё надо добавить проверку на максимальную длину строки. Мы пишем в обычном if-стиле и случайно допускаем ошибку в шаблоне:
Если в рантайме
Конечно, тут сообщение будет неполное. Но это лучше, чем совершенное левое исключение, не связанное с исходной ошибкой.
Другая фишка – это стремление Гуавы не генерировать мусора. Все помнят, что в Java есть боксинг, а это значит, что простая сигнатура
Например, в этом случае мусора не будет вообще, так как есть перегрузка checkArgument(boolean, String, int, int):
Таким образом,
1. Компактно
2. Безопасно
3. Эффективно
В общем, рекомендую. Валидировать аргументы надо, и надо делать это с хорошими сообщениями.
#guava
Есть, к примеру, следующая запись:
public record Employee(String firstName, String lastName) {}Чего здесь не хватает? Правильно, проверок на
null для firstName и lastName. Ну так давайте добавим:public record Employee(String firstName, String lastName) {
public Employee {
if (firstName == null) {
throw new NullPointerException("firstName must not be null");
}
if (lastName == null) {
throw new NullPointerException("lastName must not be null");
}
}
}Как-то слишком длинно. Если у нас в проекте сотни подобных проверок (все ж пишут проверки, так ведь?🙂), то код сильно раздувается. Хочется покомпактнее. Вспоминаем, что в Java 8 появился метод Objects.requireNonNull(). Заменяем:
public Employee {
Objects.requireNonNull(firstName, "firstName must not be null");
Objects.requireNonNull(lastName, "lastName must not be null");
}Гораздо лучше. Но теперь вспоминаем, что
firstName и lastName также не могут быть пустыми. Objects.requireNonNull() тут уже не поможет. Придётся опять писать трёхстрочные if’ы? Не хочется. Можно создать какой-нибудь утилитный метод типа checkCondition(). Но наверняка в какой-нибудь библиотеке такое уже есть? Я в течение своей многолетней практики сталкивался с разными вариантами и в конце концов понял, что всё-таки лучшим образом эту проблему решили в Guava. В классе Preconditions:public Employee {
...
Preconditions.checkArgument(!firstName.isEmpty(), "firstName must not be empty");
Preconditions.checkArgument(!lastName.isEmpty(), "lastName must not be empty");
}Есть там и другие проверки: checkNotNull(), checkElementIndex(), checkPositionIndex(), checkState(). При этом checkArgument() из них самый универсальный, и с его помощью можно проверить любое boolean выражение:
Preconditions.checkArgument(firstName != null && !firstName.isEmpty(), "firstName must not be null or empty");
Preconditions.checkArgument(lastName != null && !lastName.isEmpty(), "lastName must not be null or empty");
В итоге мы смогли уложиться в две строчки, что очень хорошо: проверки не должны занимать много места.
В Гуаве при этом решено ещё несколько проблем.
Представим, что нам ещё надо добавить проверку на максимальную длину строки. Мы пишем в обычном if-стиле и случайно допускаем ошибку в шаблоне:
if (firstName.length() > MAX_LENGTH) {
throw new IllegalArgumentException(String.format("firstName is too long, max length is %s, got %s", MAX_LENGTH));
}Если в рантайме
firstName оказался слишком длинным, то выбросится исключение, но совсем не IllegalArgumentException с красивым сообщением, а что-то совсем другое (MissingFormatArgumentException). Обидно. Гуава в этом плане более снисходительна. Вариант с Preconditions будет в любом случае бросать llegalArgumentException:Preconditions.checkArgument(firstName.length() <= MAX_LENGTH, "firstName is too long, max length is %s, got %s", MAX_LENGTH);
Конечно, тут сообщение будет неполное. Но это лучше, чем совершенное левое исключение, не связанное с исходной ошибкой.
Другая фишка – это стремление Гуавы не генерировать мусора. Все помнят, что в Java есть боксинг, а это значит, что простая сигнатура
checkArgument() с Object… создавала бы обёртки над примитивными значениями каждый раз. Мелочь, но всё равно не очень приятно. Но в Гуаве у checkArgument() есть множество перегрузок для большинства простых случаев.Например, в этом случае мусора не будет вообще, так как есть перегрузка checkArgument(boolean, String, int, int):
Preconditions.checkArgument(firstName.length() <= MAX_LENGTH, "firstName is too long, max length is %s, got %s", MAX_LENGTH, firstName.length());
Таким образом,
Preconditions в Гуаве – это:1. Компактно
2. Безопасно
3. Эффективно
В общем, рекомендую. Валидировать аргументы надо, и надо делать это с хорошими сообщениями.
#guava
👍26 2
Thread.resume() и Thread.suspend() будут окончательно удалены в Java 23. Они стали deprecated ещё аж в Java 1.2. Но удалят их только сейчас.Thread.stop() пока что остаётся, поскольку есть ещё довольно много кода, который ссылается на этот метод (правда какой толк, если stop() всё равно бросает UnsupportedOperationException с Java 19).Кстати, это скрин с сайта javaalmanac.io Очень удобный сайт, рекомендую.
🫡15👍8 4✍2🍓1
В Java 22 появится новый класс ListFormat, с помощью которого можно форматировать списки согласно указанной локали.
Также можно и парсить строки обратно в списки:
#java22
List<Integer> list = List.of(1, 2, 3, 4);
var formatDefault = ListFormat.getInstance();
var formatUS = ListFormat.getInstance(Locale.US, ListFormat.Type.STANDARD, ListFormat.Style.FULL);
var formatGerman = ListFormat.getInstance(Locale.GERMAN, ListFormat.Type.STANDARD, ListFormat.Style.FULL);
var formatUnit = ListFormat.getInstance(Locale.US, ListFormat.Type.UNIT, ListFormat.Style.SHORT);
var formatNarrow = ListFormat.getInstance(Locale.US, ListFormat.Type.UNIT, ListFormat.Style.NARROW);
var formatCustom = ListFormat.getInstance(new String[] {"{0};{1}", "{0};{1}", "{0};{1}", "", ""});
System.out.println(formatDefault.format(list)); // 1, 2, 3 и 4
System.out.println(formatUS.format(list)); // 1, 2, 3, and 4
System.out.println(formatGerman.format(list)); // 1, 2, 3 und 4
System.out.println(formatUnit.format(list)); // 1, 2, 3, 4
System.out.println(formatNarrow.format(list)); // 1 2 3 4
System.out.println(formatCustom.format(list)); // 1;2;3;4
Также можно и парсить строки обратно в списки:
List<String> list = formatUS.parse("1,2,3");
System.out.println(list); // [1,2,3]#java22
Пока мы тут обсуждаем Java 22, уже первые три JEP'а появились в Java 23:
• Финальные шаблонные строки: String Templates (Final)
• Финальные неявные классы: Implicitly Declared Classes and Instance Main Methods (Final)
• Превью примитивных типов в паттернах: Primitive types in Patterns, instanceof, and switch (Preview)
#java23
• Финальные шаблонные строки: String Templates (Final)
• Финальные неявные классы: Implicitly Declared Classes and Instance Main Methods (Final)
• Превью примитивных типов в паттернах: Primitive types in Patterns, instanceof, and switch (Preview)
#java23
Как известно, ваш фреймворк ни о чём, если не поддерживает fluent API. Поэтому, чтобы поспевать за модными трендами, в SLF4J 2.0 полтора года назад его решили добавить.
То есть если раньше писали:
То сейчас можно писать:
Это более круто и молодёжно.
Или вместо:
Можно писать:
Ну не красота ли?
Ладно, sarcasm mode off. Если серьёзно, то я действительно не вижу такой уж гигантской пользы от fluent API в SLF4J 2.0. В моём коде сейчас нет ни одного лога, где бы я использовал такие цепочки. Я продолжаю писать всё по старинке. Это короче и читабельнее. Но на ум приходит вот такой случай. Допустим, мне очень сильно важна производительность и я не хочу, чтобы мои логи генерировали какого-либо лишнего мусора. В таком случае мне бы пришлось что-то делать с логами с тремя или более параметрами:
Если уровень
Можно обернуть строку в
Здесь нет никаких массивов, а
А что вы думаете про новый fluent API в SLF4J 2.0? Используете ли? И в каких случаях?
#slf4j
То есть если раньше писали:
log.info("Hello world");То сейчас можно писать:
log.atInfo().log("Hello world");Это более круто и молодёжно.
Или вместо:
log.debug("firstName={}, lastName={}", firstName, lastName);Можно писать:
log.atDebug().addKeyValue("firstName", firstName).addKeyValue("lastName", lastName).log();Ну не красота ли?
Ладно, sarcasm mode off. Если серьёзно, то я действительно не вижу такой уж гигантской пользы от fluent API в SLF4J 2.0. В моём коде сейчас нет ни одного лога, где бы я использовал такие цепочки. Я продолжаю писать всё по старинке. Это короче и читабельнее. Но на ум приходит вот такой случай. Допустим, мне очень сильно важна производительность и я не хочу, чтобы мои логи генерировали какого-либо лишнего мусора. В таком случае мне бы пришлось что-то делать с логами с тремя или более параметрами:
log.debug("Full name is {} {} {}", firstName, middleName, lastName);Если уровень
DEBUG отключен, то в этом случае всегда будет создаваться ненужный массив, т.к. у Logger есть только перегрузки с одним и двумя параметрами, а дальше уже только Object....Можно обернуть строку в
if (log.isDebugEnabled()) {...}, но станет некрасиво. Но можно как раз использовать fluent API:log.atDebug().addArgument(firstName).addArgument(middleName).addArgument(lastName).log("Full name is {} {} {}");Здесь нет никаких массивов, а
atDebug() в рантайме всегда возвращает синглтон, так что ничего лишнего выделиться не должно. В версии SLF4J 2.1, которая скоро выйдет, код будет выглядеть покороче:log.atDebug().arg(firstName).arg(middleName).arg(lastName).log("Full name is {} {} {}");А что вы думаете про новый fluent API в SLF4J 2.0? Используете ли? И в каких случаях?
#slf4j
🤮28👍6😁5🤔1 1
Языку Scala сегодня исполнилось 20 лет. Релиз первой публичной версии Scala произошёл 20 января 2004 года.
🍾12🎉5 2
Новый черновик JEP: Derived Record Creation (Preview)
Предлагается ввести новую синтаксическую конструкцию для удобного преобразования записей:
Например, есть запись:
Пример преобразования:
Это будет логически эквивалентно (но намного короче):
#record
Предлагается ввести новую синтаксическую конструкцию для удобного преобразования записей:
e with { ... }.Например, есть запись:
record Point(int x, int y, int z) { }Пример преобразования:
Point newLoc = oldLoc with {
x *= 2;
y *= 2;
z *= 2;
};Это будет логически эквивалентно (но намного короче):
Point newLoc = switch (oldLoc) {
case Point(var x, var y, var z) -> {
x *= 2;
y *= 2;
z *= 2;
yield new Point(x, y, z);
}
};#record
🤯28 13👍4🔥3🤡2🤨2
Новые JEP'ы.
JEP 467: Markdown Documentation Comments
Предлагается ввести новый тип JavaDoc'а на основе Markdown в качестве альтернативы старому на основе HTML. Такой JavaDoc использует
Старый:
Новый:
JEP 468: Derived Record Creation (Preview)
Предлагается добавить в язык with'еры для записей:
Это особенно удобно, когда нужно изменить только один компонент записи из многих:
JEP 467: Markdown Documentation Comments
Предлагается ввести новый тип JavaDoc'а на основе Markdown в качестве альтернативы старому на основе HTML. Такой JavaDoc использует
/// вместо /** ... */:Старый:
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
*/
Новый:
/// Returns a hash code value for the object. This method is
/// supported for the benefit of hash tables such as those provided by
/// [java.util.HashMap].
JEP 468: Derived Record Creation (Preview)
Предлагается добавить в язык with'еры для записей:
record Point(int x, int y, int z) { }
Point nextLoc = oldLoc with {
x *= 2;
y *= 2;
z *= 2;
};Это особенно удобно, когда нужно изменить только один компонент записи из многих:
record User(int id, String firstName, String lastName, String email) {}
var newUser = oldUser with { id = 42 };🔥14 12👍9
Озвучено новое предложение по String Templates. Предлагается избавиться от интерфейса Processor и оставить только StringTemplate. То есть больше не будет никаких
Чтобы это стало возможным, библиотекам нужно будет добавить явную поддержку аргументов StringTemplate:
Однако пока остаётся открытым вопрос, что делать, если нужно просто интерполировать шаблонную строку в String. Похоже, что будет какой-то новый метод вроде join():
STR."...", RAW."..." и FMT."...", и можно будет просто писать:System.out.println("Hello \{name}");
String s = String.format("Hello %12s\{name}");Чтобы это стало возможным, библиотекам нужно будет добавить явную поддержку аргументов StringTemplate:
public class PrintStream {
...
public void println(String);
public void println(StringTemplate);
}
public class String {
...
public String format(String, Object...);
public String format(StringTemplate);
}Однако пока остаётся открытым вопрос, что делать, если нужно просто интерполировать шаблонную строку в String. Похоже, что будет какой-то новый метод вроде join():
String s = "Hello \{name}".join();👍5 4😁2