Kotlin对Spring的支持及空安全性相关技术疑问

1. 泛型类型参数的空安全不受支持
Kotlin的泛型类型参数本身没法被编译器强制做空安全校验,简单说就是你声明了List<String>,编译器没法保证列表里的元素一定是非空的,也没法约束泛型参数本身的空性传递。
不被支持的写法示例:
- 泛型类无法通过参数本身限制空性:
class Box<T>(val content: T) // 即使想让T必须非空,编译器也拦不住你传入null val nullBox = Box<String?>(null) val boxWithNull = Box<String>(null as String) // 强制转型后编译器检测不到
- 从Java调用Kotlin泛型方法时,Java的空值可以随意传入,Kotlin编译器不会拦截:
比如Java调用Kotlin的fun <T> process(item: T),直接传null也不会触发空安全检查。
2. 可变参数的空安全不受支持
可变参数(vararg)的元素空性没法被编译器完全校验,尤其是把数组传入可变参数时,数组里的空值不会被检测出来。
不被支持的写法示例:
fun printAll(vararg items: String) { items.forEach { println(it.length) } } val arrayWithNull = arrayOf("a", null, "c") printAll(*arrayWithNull) // 编译器不报错,但运行时会因为null调用length抛出NPE
这里把带null的数组展开传入可变参数,编译器不会检查每个元素的空性,直接导致运行时异常。
3. 数组元素的空安全不受支持
Kotlin数组的元素空性检查有局限,比如Array<String>理论上存非空String,但你可以通过强制转型或者Java代码插入null,编译器根本拦不住。
不被支持的写法示例:
val nonNullArray = Array(3) { "" } // 强制转型后就能放入null,编译器不报错 (nonNullArray as Array<String?>)[0] = null println(nonNullArray[0].length) // 运行时触发NPE
另外,从Java代码里可以直接给Kotlin的Array<String>赋值null元素,Kotlin编译器没法提前检测。
官方说的“空性声明甚至在小版本间也会演进”,意思是Spring Framework的小版本更新(比如从5.3.20到5.3.21)里,可能会对API的空性注解做这些调整:
- 原来没有空性注解的方法参数/返回值,新增了
@NonNull或@Nullable注解; - 原来标记为
@Nullable的元素,因为Spring团队发现它实际上不会返回null,就改成了@NonNull; - 反过来,原来标记为
@NonNull的元素,可能因为逻辑调整改成@Nullable。
当你开启-Xjsr305=strict模式时,Kotlin编译器会严格按照Spring的空性注解推断类型。如果Spring小版本更新了空性注解,你的代码可能突然出现新的空安全警告或错误:
比如之前Spring的SomeService.getConfig()方法没有空性注解,strict模式下Kotlin会推断为Config!(平台类型),你可以直接调用方法;但如果小版本给这个方法加上了@Nullable,strict模式下Kotlin会推断为Config?,你必须做空判断才能调用方法,否则编译器直接报错。
内容的提问来源于stack exchange,提问作者Dream

