因为jspecify,无法在Kotlin中实现ResultSetExtractor

移动开发 2026-07-09

我不能让一个类在Kotlin中实现Spring的 ResultSetExtractor,原因在于Kotlin如何在泛型中解释 T extends @Nullable

使用Kotlin 2.3.21和 Spring 7.0.7。

import org.springframework.jdbc.core.ResultSetExtractor
import java.sql.ResultSet


class CustomRSE<T> : ResultSetExtractor<T> {
    override fun extractData(rs: ResultSet): T? {
        return null
    }
}

错误是

返回类型 'fun extractData(rs: ResultSet): T?' 不是在 'com.infobip.mdp.CustomRSE' 中定义的被覆盖成员 'fun extractData(rs: ResultSet): T' 的返回类型的子类型。

这哪里错了?!唯一的解决办法是 -Xjspecify-annotations=warn

这里是谁的错?Kotlin还是Spring还是jspecify?Kotlin故意忽略了该注解。

https://kotlinlang.org/docs/java-interop.html#jspecify-support

该注解不适用于类型变量(泛型)。类型变量在提供具体的可空或非空类型之前,保持对空值不敏感。

作为参考,这里是Spring 7 ResultSetExtractor 接口:

@FunctionalInterface
public interface ResultSetExtractor<T extends @Nullable Object> {
    /**
     * @return an arbitrary result object, or {@code null} if none
     * (the extractor will typically be stateful in the latter case).
     */
    T extractData(ResultSet rs) throws SQLException, DataAccessException;
}

解决方案

你代码中的类型参数 TCustomRSE<T> 指定为没有具体的上界。根据定义,这意味着实际的上界将是 Any?:所有类型都被允许,甚至包括可空类型。这意味着在你实际实例化对象的任何地方,你都可以在这里使用一个可空类型。比如:

val rse = CustomRSE<String?>()

CustomRSE 实现 ResultSetExtractor,并且你在该泛型接口上使用相同的类型参数。当你实现该接口时,你不能让 extractData 返回一个 T?,因为接口声明只有 T 可以被返回。一个 T 本身也可能是可空的,但如果你使用一个非空类型——比如 String——那么 extractData 就永远不能返回 String?。这就是为什么把返回类型改为 T? 不被允许,否则你以后可能会遇到空指针异常。

按照你声明的 CustomRSE,在你尝试时你不能显式返回 null。只有在运行时你实际用一个可空的类型实例化 CustomRSE,并且在 extractData 内部以某种方式拿到这样的实例时,才会返回 null。然后你可以在那儿返回它,并且在那个时刻它可能为null。

然而,如果你希望在 extractData 中能够为任意使用的类型参数 T 显式 返回 null,那么你必须明确声明:

class CustomRSE<T> : ResultSetExtractor<T?> {
    override fun extractData(rs: ResultSet): T? {
        return null
    }
}

在这里你不仅告诉 ResultSetExtractor 允许为 CustomRSE 选择的类型 T(例如,非空的 String),而且你还额外允许 null

现在你可以将 extractData 的返回类型声明为 T?,并在其实现体中显式返回 null

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章