使用ModelMapper转换泛型Wrapper<City>到Wrapper<CityDto>失败排查
ModelMapper转换泛型Wrapper到Wrapper时嵌套列表未正确转换的原因
问题根源
ModelMapper处理泛型容器类时,默认只做浅层次的字段匹配,不会自动递归处理泛型参数的深层转换:
- 当你用
TypeToken<Wrapper<CityDto>>指定目标类型时,ModelMapper能正确创建Wrapper<CityDto>实例,并直接拷贝count字段(因为类型都是Integer,完全匹配)。 - 但对于
entities字段,源是List<City>、目标是List<CityDto>,由于Java泛型的类型擦除特性,运行时ModelMapper无法自动识别出需要把列表里的每个City转成CityDto,而是直接把源列表的元素原样赋值给目标Wrapper的entities字段,导致列表元素还是City类型,强转成CityDto时触发ClassCastException。
临时方案有效的原因
你的手动转换逻辑是先单独处理列表转换,再封装到Wrapper中:
- 当你用
new TypeToken<List<CityDto>>() {}.getType()明确指定列表的目标类型时,ModelMapper能清楚知道需要遍历列表,把每个City转换成CityDto,因此能正确完成元素转换。
可选优化方案(无需手动封装)
如果不想每次都拆分转换步骤,可以给ModelMapper注册自定义转换器,显式处理Wrapper的泛型转换逻辑:
ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE); // 注册针对Wrapper<City>到Wrapper<CityDto>的转换器 modelMapper.addConverter(new Converter<Wrapper<City>, Wrapper<CityDto>>() { @Override public Wrapper<CityDto> convert(MappingContext<Wrapper<City>, Wrapper<CityDto>> context) { Wrapper<City> source = context.getSource(); Wrapper<CityDto> target = new Wrapper<>(); target.setCount(source.getCount()); // 单独转换列表元素 List<CityDto> dtoList = modelMapper.map(source.getEntities(), new TypeToken<List<CityDto>>() {}.getType()); target.setEntities(dtoList); return target; } }); // 之后直接调用map即可完成完整转换 Wrapper<City> sourceWrapper = ...; Wrapper<CityDto> result = modelMapper.map(sourceWrapper, Wrapper.class);
如果需要支持多种类型的Wrapper转换,可以写一个通用的转换器工具方法:
// 通用Wrapper转换器生成方法 public static <S, D> Converter<Wrapper<S>, Wrapper<D>> createWrapperConverter(ModelMapper mapper, Class<D> destClass) { return context -> { Wrapper<S> source = context.getSource(); Wrapper<D> target = new Wrapper<>(); target.setCount(source.getCount()); List<D> dtoList = mapper.map(source.getEntities(), new TypeToken<List<D>>() {}.getType()); target.setEntities(dtoList); return target; }; } // 使用示例 modelMapper.addConverter(createWrapperConverter(modelMapper, CityDto.class));
内容的提问来源于stack exchange,提问作者JavaFan
相关产品推荐
相关产品推荐

