Java中如何避免子类转换DTO与Model时的强制类型转换问题
优化方案
方案1:泛型化转换器接口 + 转换器路由(自研轻量实现)
步骤1:改造转换器接口,增加泛型约束
从接口层面限定每个转换器对应的业务模型和DTO类型,从根源避免强转:
// 泛型参数分别为:业务模型类型、DTO类型 public interface AnswerConverter<M extends Answer, D extends AnswerDto> { D toDto(M model); M toModel(D dto); }
步骤2:改造转换器实现类,指定泛型参数
实现转换器时直接绑定对应子类类型,不需要任何强制转换代码:
public class AnswerMapConverter implements AnswerConverter<AnswerMap, AnswerMapDto> { @Override public AnswerMapDto toDto(AnswerMap answer) { AnswerMapDto answerMapDto = new AnswerMapDto(); answerMapDto.setValues(answer.getValues()); return answerMapDto; } @Override public AnswerMap toModel(AnswerMapDto answerDto) { AnswerMap answerMap = new AnswerMap(); answerMap.setValues(answerDto.getValues()); return answerMap; } }
后续新增其他子类(比如返回String类型的AnswerText/AnswerTextDto),只要按照相同模式新增转换器实现即可,所有实现类都不需要强转。
步骤3:实现转换器注册路由,统一转换入口
提供上层统一调用入口,自动根据入参类型匹配对应转换器,业务代码不需要感知具体转换器实现:
import java.util.HashMap; import java.util.Map; public class AnswerConverterRegistry { // 存储业务模型类 -> 对应转换器的映射 private final Map<Class<? extends Answer>, AnswerConverter<?, ?>> modelToConverterMap = new HashMap<>(); // 存储DTO类 -> 对应转换器的映射 private final Map<Class<? extends AnswerDto>, AnswerConverter<?, ?>> dtoToConverterMap = new HashMap<>(); // 注册转换器,Spring环境下可以通过依赖注入所有AnswerConverter实例自动完成注册 public <M extends Answer, D extends AnswerDto> void registerConverter(Class<M> modelClass, Class<D> dtoClass, AnswerConverter<M, D> converter) { modelToConverterMap.put(modelClass, converter); dtoToConverterMap.put(dtoClass, converter); } // 业务模型转DTO统一入口 @SuppressWarnings("unchecked") public <M extends Answer, D extends AnswerDto> D convertToDto(M model) { AnswerConverter<M, D> converter = (AnswerConverter<M, D>) modelToConverterMap.get(model.getClass()); if (converter == null) { throw new IllegalArgumentException("未找到对应类型的转换器:" + model.getClass().getName()); } return converter.toDto(model); } // DTO转业务模型统一入口 @SuppressWarnings("unchecked") public <M extends Answer, D extends AnswerDto> M convertToModel(D dto) { AnswerConverter<M, D> converter = (AnswerConverter<M, D>) dtoToConverterMap.get(dto.getClass()); if (converter == null) { throw new IllegalArgumentException("未找到对应类型的转换器:" + dto.getClass().getName()); } return converter.toModel(dto); } }
方案2:使用成熟映射工具(降低手写代码量)
如果不想自行维护转换器逻辑,可以使用MapStruct这类编译期映射工具,自动生成转换器实现,连转换逻辑都不需要手写:
import org.mapstruct.Mapper; @Mapper public interface AnswerMapConverter { AnswerMapDto toDto(AnswerMap model); AnswerMap toModel(AnswerMapDto dto); }
编译时MapStruct会自动生成无强转的实现类,新增子类时仅需新增对应Mapper接口即可。
优化收益
- 编译期即可校验类型合法性,彻底避免运行时强转异常
- 新增子类时仅需新增对应模型、DTO、转换器实现,符合开闭原则
- 上层业务无需感知具体转换器实现,调用逻辑统一
内容的提问来源于stack exchange,提问作者Koin Arab
相关产品推荐
相关产品推荐

