microJUG – Telegram
microJUG
979 subscribers
155 photos
1 video
2 files
237 links
Мысли о Java.
Основной канал: @miniJUG
Буст: https://news.1rj.ru/str/microJUG?boost
Чат: https://news.1rj.ru/str/micro_JUG
Таблица JEP'ов: https://minijug.org/jeps.html
Download Telegram
Сравнение производительности сборщиков мусора в 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 исчезнет полностью.
👍1210🔥7
Давайте поговорим про валидацию входных аргументов. На первый взгляд тема кажется совсем банальной, но есть в ней несколько нюансов, которым, на мой взгляд, уделяют недостаточно внимания.

Есть, к примеру, следующая запись:
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
👍262
Thread.resume() и Thread.suspend() будут окончательно удалены в Java 23. Они стали deprecated ещё аж в Java 1.2. Но удалят их только сейчас.
Thread.stop() пока что остаётся, поскольку есть ещё довольно много кода, который ссылается на этот метод (правда какой толк, если stop() всё равно бросает UnsupportedOperationException с Java 19).
Кстати, это скрин с сайта javaalmanac.io Очень удобный сайт, рекомендую.
🫡15👍842🍓1
В Java 22 появится новый класс ListFormat, с помощью которого можно форматировать списки согласно указанной локали.

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
17🔥8
Пока мы тут обсуждаем 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
22👍1😨1
Как известно, ваш фреймворк ни о чём, если не поддерживает fluent API. Поэтому, чтобы поспевать за модными трендами, в SLF4J 2.0 полтора года назад его решили добавить.

То есть если раньше писали:
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🤔11
Языку Scala сегодня исполнилось 20 лет. Релиз первой публичной версии Scala произошёл 20 января 2004 года.
🍾12🎉52
Новый черновик JEP: Derived Record Creation (Preview)

Предлагается ввести новую синтаксическую конструкцию для удобного преобразования записей: 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
🤯2813👍4🔥3🤡2🤨2
Новые JEP'ы.

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 };
🔥1412👍9
Озвучено новое предложение по String Templates. Предлагается избавиться от интерфейса Processor и оставить только StringTemplate. То есть больше не будет никаких 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();
👍54😁2
Пользователям macOS Sonoma на Apple Silicon не рекомендуется обновляться до версии 14.4, так как после этого Java-процессы могут неожиданно терминироваться. Проблема затрагивает все версии Java с 8 по 22. Обходного пути нет. Также после обновления у вас не будет простого способа откатиться на предыдущую версию macOS.
#macos
🤣138💩4👏3🤬2🔥1
Завтра что-то выйдет
😱17🎉127👻6
Ежегодный опрос. Какую версию(-и) Java или язык JVM вы используете на работе?
Anonymous Poll
16%
Java 8 или более старую
24%
Java 11
0%
Java 12-16
62%
Java 17
2%
Java 18-20
26%
Java 21
1%
Java 22
18%
Kotlin
2%
Scala
5%
Groovy
4🤯1
Спрингу исполнилось 20 лет. 24 марта 2004 года вышел Spring Framework 1.0 Final.
🎉39🍾5🔥2🤯1
Сегодня с ребятами из каналов @javaswag и @staff_engineers будем обсуждать новые фичи в Java 22. Стрим начнётся в 20:30 по Москве. Приходите, будет интересно.

https://www.youtube.com/watch?v=8E_g-VHJCtw
5👍31🥱1🥴1
Баг с крэшем JVM исправили в macOS 14.4.1. Так что теперь можно обновляться.
#macos
🔥1411🌚3👍1
Новый черновик JEP: Module Import Declarations (Preview).

Предлагается добавить новый тип импорта: импорт всех экспортированных пакетов из модуля. Например:

import module java.base;


Это то же самое, что вручную написать import для всех пакетов, экспортированных в модуле java.base:

import java.io.*;
import java.util.*;
import java.util.function.*;
... // ещё 51 импорт
💩9🤯8🔥5🤔44
Брайан Гетц написал огромное письмо в рассылку, где впервые очень подробно и с конкретными примерами синтаксиса написал, как могут выглядеть member-паттерны (паттерны-члены). Напомню, что в Java сейчас есть только паттерны у записей. Member-паттерны позволят объявлять паттерны в любых классах. Рассмотрим несколько примеров:

Паттерн-деконструктор:
class Point {
int x, y;

Point(int x, int y) {
this.x = x;
this.y = y;
}

pattern Point(int x, int y) {
matches Point(that.x, that.y);
}
}


Статические паттерны:
class Optional<T> {
static<T> Optional<T> of(T t) { ... }
static<T> Optional<T> empty() { ... }

static<T> case pattern(Optional<T> that) of(T t) {
if (that.isPresent())
matches of(that.get());
}

static<T> case pattern(Optional<T> that) empty() {
if (that.isEmpty())
matches empty();
}
}


(В примере выше case нужен для проверки exhaustiveness компилятором)

Далее такие статические паттерны можно уже применять в switch или instanceof:
case Optional.of(var e): ...
case Optional.empty(): ...


Инстанс-паттерны:
public class Class<T> {
...

pattern arrayClass(Class<?> componentType) {
if (that.isArray())
matches arrayClass(that.getComponentType());
}
}


package java.util.regex;

public class Pattern
...
pattern(String that) regexMatch(String... groups) {
Matcher m = matcher(that);
if (m.matches())
matches Pattern.regexMatch(IntStream.range(1, m.groupCount())
.map(Matcher::group)
.toArray(String[]::new));
}
}


Использование:
int[] array = {1,2,3,4,5};
Class<?> clazz = array.class;
if (clazz instanceof arrayClass(componentType)) {
...
}

static Pattern As = Pattern.compile("(a*)");
static Pattern Bs = Pattern.compile("(b*)");
...

String str = "aaaa";
switch (str) {
case As.regexMatch(var as): ...
case Bs.regexMatch(var bs): ...
...
}


Как-то так.

#patternmatching
🤯27👍7💩5👀4🤮22🤔1
🔥14👌9👍3👏1
😥 String Templates всё. В Java 23 их не будет, даже с --enable-preview.
😢17👀10👍8😁5🤯5🍾4🔥1
⚰️ JetBrains закрывает Space. Причина – низкая востребованность продукта. Новые подписки больше не продаются, а для существующих будет работать поддержка и будут выходить критические обновления до 31 мая 2025 года.
Вместо Space JetBrains будет развивать новый продукт SpaceCode, который будет платформой для Git-хостинга и код-ревью (аналог GitHub/GitLab).
#jetbrains #spacecode
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍2👨‍💻1