Spring为何错误将密封类ErrorDto强制转换为Collection?
核心原因:Spring消息转换的泛型擦除与类型解析冲突
你的问题本质出在Spring消息转换器对泛型类型的处理逻辑,结合你定义的密封接口与ResponseEntity的继承结构,具体拆解如下:
泛型擦除导致的类型歧义
运行时Java/Kotlin的泛型信息会被擦除,当你的接口返回Response<List<String>>时,Spring只能看到顶层的Response接口和被擦除后的ResponseEntity父类,无法直接确定实际返回的是SuccessResponse<List<String>>还是ErrorResponse。ResponseEntity的继承结构冲突
你的SuccessResponse<T>继承ResponseEntity<T>,ErrorResponse继承ResponseEntity<ErrorDto>——这意味着Response接口的两个实现类,对应的ResponseEntity泛型参数完全不同:一个是任意泛型T,另一个是固定的ErrorDto。
当Spring处理集合类型的返回值时,它会尝试按照ResponseEntity<Collection>的逻辑去匹配转换器,但此时ErrorResponse作为Response的实现类,它的泛型参数是ErrorDto,和Collection完全不兼容,消息转换器在尝试序列化时就会抛出ErrorDto无法转换为Collection的异常。自定义类包裹List能解决的原因
当你用自定义类(比如StringListWrapper(val list: List<String>))包裹List后,SuccessResponse<StringListWrapper>对应的ResponseEntity泛型参数是具体的自定义类,不再是模糊的Collection类型。Spring能明确区分SuccessResponse<StringListWrapper>和ErrorResponse的类型差异,不会出现类型匹配冲突,因此可以正常序列化。
额外补充:密封类在这里的影响
Kotlin密封类虽然在编译时保证了所有实现类都在同一模块,但Spring的消息转换是运行时行为,编译时的类型约束无法直接传递给Spring的类型解析器。Spring依然需要通过运行时的类型信息来选择转换器,这就导致了泛型擦除后的类型歧义问题无法被密封类本身解决。
内容的提问来源于stack exchange,提问作者OroshiX

