SpringBoot+MyBatis中Mapper返回类型与Java声明不一致却正常运行的原因
现象原理拆解:MyBatis返回子类实例匹配父类声明的原因
1. Java泛型的类型擦除机制
Java泛型是编译时语法糖,编译完成后泛型信息会被彻底擦除。你代码里的List<Menu>,编译后会退化为原始类型List。这带来两个关键影响:
- 编译阶段编译器仅做静态类型检查,只要
MenuView是Menu的子类(满足向上转型规则),就不会报错;但它无法约束运行时List中实际存放的实例类型。 - 运行时JVM不知道泛型的具体类型限制,只要实例是
Menu的子类,就能正常存入List,不会触发类型转换异常。
2. MyBatis的对象创建逻辑
MyBatis执行查询时,完全以XML配置的resultType为准来实例化对象:
- 你在
findAll的XML里指定了resultType="MenuView",MyBatis就会通过反射直接创建MenuView实例,再把查询到的所有字段(包括likeCount)填充进去。 - MyBatis不会校验Mapper接口声明的返回类型(
List<Menu>)和resultType是否完全一致,只要两者存在继承/实现关系,就会直接返回该实例。
3. 编译时检查的局限性
编译器只能校验代码语法和静态类型的合法性:
- 它无法读取MyBatis XML中的
resultType配置,只会判断MenuView能否向上转型为Menu(子类转父类是合法的),因此不会抛出编译错误。 - 这种“接口声明父类、实际返回子类”的写法本身符合Java多态规则,编译器没有理由拦截。
4. 运行时字段/属性的访问逻辑
页面能正常打印likeCount,核心是运行时操作的是MenuView实例:
- 如果是通过Spring Boot返回JSON到页面,序列化框架(如Jackson)会反射运行时对象的所有字段,不管你代码里声明的是
Menu类型,因此likeCount会被正常序列化输出。 - 即便直接调用
getLikeCount()方法,由于运行时对象是MenuView,会触发动态绑定,调用子类的方法(若Menu中无此方法,编译时会报错,能正常运行说明要么Menu有该方法的空实现,要么你是通过反射访问字段)。
内容的提问来源于stack exchange,提问作者unrest7972
相关产品推荐
相关产品推荐

