Joo*_*oer 5 java generics jdbi java-21
还有其他人也注意到 Java 21 中 Java 泛型的变化吗?
我们使用Jdbi进行数据库访问,当更新到 Java 21 时,会出现问题,因为无法再确定泛型类型。
在调试时我注意到一个条件
java.lang.reflect.Type type = ...
if (type instanceof Class) {..}
Run Code Online (Sandbox Code Playgroud)
其中type是一个接口,在 Java 17 中计算结果为false,但true在 Java 21 中计算结果为 。
Jdbi的某些泛型依赖于Geantyref库。静态方法GenericTypeReflector.getTypeParameter(..)在 Java 17 中返回泛型类型,但null在 Java 21 中返回。
在发行说明或其他公告中,我找不到任何有关 Java 21 中有关泛型的更改的信息。也没有发现向后兼容的重大更改。
我假设一些内部结构可能由于记录反射 (JEP-440) 或开关的模式匹配 (JEP-441) 而发生了变化,但我已经将这些功能用作 17 预览版功能,没有任何问题。
有人知道是什么导致了这些变化以及为什么吗?
我想我已经明白了。我将问题追溯到记录类的构造函数参数。似乎在 Java 21 中,记录类的默认构造函数的参数不知何故不具有泛型类型。所以对于像这样的记录
record Person(Optional<String> name, Optional<Integer> age) {}
Run Code Online (Sandbox Code Playgroud)
泛型类型String并Integer在 Person 的默认构造函数中丢失。可以通过显式添加具有所有记录属性的规范构造函数来更改 Java 生成的默认构造函数:
record Person(Optional<String> name, Optional<Integer> age) {
@JdbiConstructor
public Person(Optional<String> aName, Integer anAge) {
this(aName, anAge);
}
}
Run Code Online (Sandbox Code Playgroud)
通过添加@JdbiConstructor注释(Jdbi 在记录上始终需要该注释),指示 Jdbi 使用哪个构造函数。由于此构造函数现在显式定义了包括泛型类型在内的参数类型,因此反射 util 能够确定类型,并且我让它可以与 Java 21 一起使用。
在过去的两个小时里,我一直在尝试创建一个最低限度的测试用例,以验证泛型类型在记录的默认构造函数参数中是否丢失,但我无法在测试中重现它。即使它在我们的应用程序中始终失败。我在同时调试应用程序和测试时一直在比较类型参数。参数值和类型看起来相同,但测试在 Java 21 中仍然成功。
我暂时离开测试,因为我必须继续我的工作,现在我通过添加额外的构造函数找到了一个可行的解决方法。我稍后会尝试回到这个话题。它必须能够在简单的测试中重现。
感谢 Holger 创建了一个小测试用例来证明这个问题。
我在 11 月 17 日的更新中出现了两处错误:
我已相应地更新了 11 月 17 日的更新。感谢您指出这些错误。
我已经提交了一份带有 ID 的错误报告9076247,目前正在审核中。
报告的错误已经过评估并分配了错误 ID:JDK-8320575,现在在 url JDK-8320575上可见
应用一些更正后我可以重现您的问题:
\n
使用\n \n时不会出现问题,但record Person(Optional<String> name, Optional<Integer> age) {}
既然你说,Jdbi 总是需要@JdbiConstructor记录上的注释,我假设你提供了一个带注释的紧凑构造函数。然后,问题就出现了。
你的修复根本不起作用。当交换两个类型的参数时Optional,您会得到一个与规范构造函数具有相同擦除的构造函数。这是不被正确的编译器接受的,即使编译器接受它,它也会在运行时中断。
规范构造函数不能被修复是不正确的。您可以创建一个显式的规范构造函数而不是紧凑的构造函数。
\n使用以下测试程序:
\nimport java.util.Optional;\n\npublic class Reproducer {\n interface NoConstructorDeclarations {\n record Person(Optional<String> name, Optional<Integer> age) {}\n }\n\n interface AnnotatedCompactConstructor {\n record Person(Optional<String> name, Optional<Integer> age) {\n @Deprecated public Person {}\n }\n }\n\n interface AnotatedExplicitCanonicalConstructor {\n record Person(Optional<String> name, Optional<Integer> age) {\n @Deprecated\n public Person(Optional<String> name, Optional<Integer> age) {\n this.name = name;\n this.age = age;\n }\n }\n }\n\n public static void main(String args[]) {\n for(var approach: Reproducer.class.getDeclaredClasses()) {\n Class<?> recordClass = approach.getClasses()[0];\n System.out.println(approach.getSimpleName());\n \n var constructor = recordClass.getConstructors()[0];\n System.out.println(constructor.isAnnotationPresent(Deprecated.class));\n for(var p: constructor.getParameters()) {\n System.out.println(p);\n }\n System.out.println();\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n从javacJDK\xc2\xa021 中,我得到:
AnotatedExplicitCanonicalConstructor\ntrue\njava.util.Optional<java.lang.String> name\njava.util.Optional<java.lang.Integer> age\n\nAnnotatedCompactConstructor\ntrue\n java.util.Optional name\n java.util.Optional age\n\nNoConstructorDeclarations\nfalse\njava.util.Optional<java.lang.String> name\njava.util.Optional<java.lang.Integer> age\nRun Code Online (Sandbox Code Playgroud)\n它承认了该问题并演示了解决方法。以前的 JDK 和 Eclipse\xe2\x80\x99s 编译器都不会出现此问题javac。
我使用,@Deprecated来代替@JdbiConstructor, 以使示例独立于第三方库。
| 归档时间: |
|
| 查看次数: |
538 次 |
| 最近记录: |