还有没有人注意到Java 21中Java泛型的变化?

我们使用Jdbi来访问数据库,当更新到Java 21时,由于泛型类型无法确定,所以会出错.

在调试过程中,我注意到一个条件

java.lang.reflect.Type type = ...
if (type instanceof Class) {..}

其中type是一个接口,在Java 17中的计算结果为false,但在Java 21中为true.

JDbi的一些泛型依赖于Geantyref库.静态方法GenericTypeReflector.getTypeParameter(..)在Java 17中返回泛型类型,但在Java 21中返回null.

在发布说明或其他公告中,我找不到任何关于Java 21中泛型的变化.也没有向后兼容的 destruct 性变化.

我假设由于记录反射(JEP-440)或switch 的模式匹配(JEP-441),一些内部功能可能已经改变,但这些功能我已经作为17预览功能使用,没有任何问题.

有人知道是什么导致了这些变化吗?为什么?

更新17-2023年11月

我想我已经想通了.我将问题追溯到Record类的构造函数参数. 似乎在Java 21中,对于记录类,默认构造函数的参数不知何故没有泛型类型. 所以对于像这样的唱片来说

record Person(Optional<String> name, Optional<Integer> age) {}

泛型类型StringInteger在Person的默认构造函数中丢失. 通过显式添加具有所有记录属性的规范构造函数,可以更改Java生成的默认构造函数:

record Person(Optional<String> name, Optional<Integer> age) {
  @JdbiConstructor
  public Person(Optional<String> aName, Integer anAge) {
    this(aName, anAge);
  }
}

通过添加@JdbiConstructor注释(Jdbi总是需要记录中的注释),可以指示Jdbi使用哪个构造函数. 因为这个构造函数现在显式定义了参数类型,包括泛型类型,所以反射util能够确定类型,我让它与Java 21一起工作.

在过go 的2个小时里,我一直试图创建一个最小的测试用例,以验证泛型类型在Record的默认构造函数参数中丢失,但我无法在测试中重现它.即使它在我们的应用程序中始终失败. 我一直在比较类型参数,同时调试应用程序和测试.参数值和类型看起来相同,但在Java 21中测试仍然成功.

我现在离开测试,因为我必须继续我的工作,现在我确实有了一个通过添加额外的构造函数来解决问题的方法. 我稍后会试着回到这个问题上.它必须在简单的测试中是可重现的.

更新20-2023年11月

感谢Holger创建了一个小型测试用例来证明这个问题.

在11月17日的更新中,我犯了两个错误:

  • 交换两个可选参数确实会创建一个不同的构造函数,因为被擦除的类型是相等的.
  • 通过显式地提供规范构造函数来覆盖默认记录构造函数can.

我已经相应地更新了17日的更新.谢谢你指出这些错误.

我已经提交了一份ID为9076247的错误报告,目前正在审查中.

推荐答案

我可以在应用一些更正后重现您的问题:

  • The problem does not occur when using
    record Person(Optional<String> name, Optional<Integer> age) {}
    But since you said, Jdbi always requires the @JdbiConstructor annotation on a record, I assume that you provided an annotated compact constructor. Then, the problem occurs.

  • 你的解决办法根本不管用.当交换两个类型均为Optional的参数时,您将得到一个与规范构造函数具有相同擦除的构造函数.这不被正确的编译器接受,即使编译器接受它,它也会在运行时中断.

  • 不能修复规范构造函数是不正确的.您可以创建显式规范构造函数,而不是紧凑构造函数.

使用以下测试程序:

import java.util.Optional;

public class Reproducer {
    interface NoConstructorDeclarations {
        record Person(Optional<String> name, Optional<Integer> age) {}
    }

    interface AnnotatedCompactConstructor {
        record Person(Optional<String> name, Optional<Integer> age) {
            @Deprecated public Person {}
        }
    }

    interface AnotatedExplicitCanonicalConstructor  {
        record Person(Optional<String> name, Optional<Integer> age) {
            @Deprecated
            public Person(Optional<String> name, Optional<Integer> age) {
                this.name = name;
                this.age = age;
            }
        }
    }

    public static void main(String args[]) {
        for(var approach: Reproducer.class.getDeclaredClasses()) {
            Class<?> recordClass = approach.getClasses()[0];
            System.out.println(approach.getSimpleName());
  
            var constructor = recordClass.getConstructors()[0];
            System.out.println(constructor.isAnnotationPresent(Deprecated.class));
            for(var p: constructor.getParameters()) {
                System.out.println(p);
            }
            System.out.println();
        }
    }
}

和来自JDK 21的javac,我得到:

AnotatedExplicitCanonicalConstructor
true
java.util.Optional<java.lang.String> name
java.util.Optional<java.lang.Integer> age

AnnotatedCompactConstructor
true
 java.util.Optional name
 java.util.Optional age

NoConstructorDeclarations
false
java.util.Optional<java.lang.String> name
java.util.Optional<java.lang.Integer> age

它承认了问题并演示了解决方法.这个问题不会在javac个以前的JDK中出现,也不会出现在Eclipse的编译器中.

我使用了@Deprecated而不是@JdbiConstructor,以使该示例独立于第三方库.

Java相关问答推荐

Android视图覆盖不阻止点击它后面的控件

Java WireMock定义存根在Cucumber并行执行的多线程测试中失败

如何转换Tue Feb 27 2024 16:35:30 GMT +0800 String至ZonedDateTime类型""

neo4j java驱动程序是否会在错误发生时自动回滚事务?

CAMEL 4中的SAXParseException

SpringBoot+Java 17@Valid未验证POJO

GSON期间的Java类型擦除

匹配一组字符或另一组字符

try 从REST API返回对象列表时出错

使用正则表达式从字符串中提取多个值

为什么有两种实现来检索数组类的组件类型?

S数学.exp的相同错误保证也适用于StrictMath.exp吗?

从泛型枚举创建EnumMap

JFree Chart从图表中删除边框

除0错误/抱歉我的句子是PT

通过/失败的参数化junit测试方法执行数

为什么Spring要更改Java版本配置以及如何正确设置?

如何在MPAndroidChart中的条形图上正确添加标签

无泄漏函数的Java DRY

[Guice/MissingImplementation]:未绑定任何实现