Несовместимые изменения в дженериках в Java 21
Кто-нибудь еще заметил изменения в дженериках Java в Java 21?
Мы используем Jdbi для доступа к базе данных, и при обновлении до Java 21 что-то идет не так, потому что универсальный тип больше не может быть определен.
Во время отладки я заметил, что условие
java.lang.reflect.Type type = ...
if (type instanceof Class) {..}
гдеtype
это интерфейс, который оценивается какfalse
в Java 17, но естьtrue
в Яве 21.
Некоторые из дженериков Jdbi зависят от библиотеки Geantyref.Статический метод GenericTypeReflector.getTypeParameter(..) возвращает универсальный тип в Java 17, но возвращаетnull
в Яве 21.
Нигде в примечаниях к выпуску или других объявлениях я не могу найти ничего о том, что в Java 21 что-то изменилось в дженериках. И о том, что есть обратно совместимые критические изменения.
Я предполагаю, что некоторые внутренние компоненты могли измениться из-за отражения записи (JEP-440) или сопоставления шаблонов для переключателя (JEP-441), но эти функции я уже использую в качестве функций 17-предварительного просмотра без каких-либо проблем.
Кто-нибудь знает, что вызвало эти изменения и почему?
Обновление от 17 ноября 2023 г.
Думаю, я понял это. Я связываю проблему с аргументами конструктора класса записи. Кажется, что в Java 21 для класса записи аргументы конструктора по умолчанию почему-то не имеют универсального типа. Итак, для записи типа
record Person(Optional<String> name, Optional<Integer> age) {}
общие типыString
иInteger
теряются в конструкторе Person по умолчанию. Конструктор по умолчанию, созданный Java, можно изменить, явно добавив канонический конструктор со всеми свойствами записи:
record Person(Optional<String> name, Optional<Integer> age) {
@JdbiConstructor
public Person(Optional<String> aName, Integer anAge) {
this(aName, anAge);
}
}
Добавив@JdbiConstructor
аннотация (которую Jdbi всегда требует для записи) Jdbi указывает, какой конструктор использовать. Поскольку этот конструктор теперь явно определяет типы аргументов, включая общий тип, утилита отражения может определить тип, и я заставил ее работать с Java 21.
Последние два часа я пытался создать минимальный тестовый пример, чтобы убедиться, что универсальный тип потерян в аргументах конструктора записи по умолчанию, но мне не удалось воспроизвести его в тесте. Несмотря на то, что в нашем приложении он постоянно терпит неудачу. Я сравнивал аргументы типов при одновременной отладке приложения и теста. Значения и типы аргументов выглядят одинаково, но тест в Java 21 по-прежнему проходит успешно.
Я пока оставляю тест, так как мне нужно продолжить работу, и теперь у меня есть рабочий обходной путь, добавив дополнительный конструктор. Я постараюсь вернуться к этому позже. Оно должно быть воспроизводимым в простом тесте.
Обновление от 20 ноября 2023 г.
Спасибо Хольгеру за создание небольшого тестового примера, доказывающего проблему.
В обновлении от 17 ноября у меня было две ошибки:
- замена обоих необязательных аргументов не создает другой конструктор, поскольку стертые типы равны.
- конструктор записи по умолчанию можно переопределить, явно указав канонический конструктор.
Я соответствующим образом обновил обновление от 17 ноября. Спасибо, что указали на эти ошибки.
Я отправил отчет об ошибке с идентификатором9076247
который сейчас находится на рассмотрении.
Обновление от 22 ноября 2023 г.
Сообщенная ошибка была оценена, ей присвоен идентификатор ошибки: JDK-8320575, и теперь она доступна по URL-адресу JDK-8320575.