Mapstruct泛型映射报错:无法生成通配符边界结果的映射方法
修复MapStruct中带通配符泛型的转换编译错误
问题背景
需要实现CustomizationDTD与Customization的双向转换,相关实体类代码如下:
@Data public class ProductDTD { private String id; private String name; } @Data public class CustomizationDTD<T extends ProductDTD> { private final T product; private final String description; } @Data public class Customization { private String productId; private String description; }
编写的MapStruct转换接口如下:
@Mapper(componentModel = "spring") interface CustomizationConverter { @Mapping( target = "product", source = "customization.productId", qualifiedByName = "resolve") CustomizationDTD<? extends ProductDTD> convert(final Customization customization); @Mapping( target = "productId", source = "product.id") Customization convert(final CustomizationDTD<? extends ProductDTD> customizationDTD); @Named("resolve") default ProductDTD resolve(final String productId) { // CapDTD 继承自 ProductDTD CapDTD cap = capRepository.findById(productId); ... return cap; } }
编译时出现如下错误:
error: Can't generate mapping method for a wildcard extends bound result. CustomizationDTD<? extends ProductDTD> convert( ^
报错原因
MapStruct无法生成返回值为带? extends ProductDTD通配符泛型的映射方法,因为通配符无法让MapStruct确定具体要实例化的CustomizationDTD泛型类型,也就无法完成对象的创建和属性映射。
可行解决方案
方案1:使用具体泛型类型替代通配符
如果resolve方法始终返回固定的ProductDTD子类(比如示例中的CapDTD),可以直接将转换方法的返回类型改为具体的泛型参数:
修改后的转换接口:
@Mapper(componentModel = "spring") interface CustomizationConverter { @Mapping( target = "product", source = "customization.productId", qualifiedByName = "resolve") CustomizationDTD<CapDTD> convert(final Customization customization); @Mapping( target = "productId", source = "product.id") Customization convert(final CustomizationDTD<? extends ProductDTD> customizationDTD); @Named("resolve") default CapDTD resolve(final String productId) { CapDTD cap = capRepository.findById(productId); // 其他业务逻辑 return cap; } }
方案2:给Mapper接口添加泛型参数
如果需要支持多种ProductDTD子类的转换,可以给转换接口引入泛型参数,明确指定泛型类型:
@Mapper(componentModel = "spring") interface CustomizationConverter<T extends ProductDTD> { @Mapping( target = "product", source = "customization.productId", qualifiedByName = "resolve") CustomizationDTD<T> convert(final Customization customization); @Mapping( target = "productId", source = "product.id") Customization convert(final CustomizationDTD<T> customizationDTD); @Named("resolve") T resolve(final String productId); }
之后针对具体的子类实现该接口并注入Spring容器,比如针对CapDTD:
@Component public class CapCustomizationConverter implements CustomizationConverter<CapDTD> { private final CapRepository capRepository; public CapCustomizationConverter(CapRepository capRepository) { this.capRepository = capRepository; } @Override public CapDTD resolve(String productId) { CapDTD cap = capRepository.findById(productId); // 其他业务逻辑 return cap; } }
补充说明
从CustomizationDTD<? extends ProductDTD>转换到Customization的方法无需修改,MapStruct可以正常处理输入参数中的通配符泛型,问题仅出在返回值的通配符定义上。
内容的提问来源于stack exchange,提问作者j3d
相关产品推荐
相关产品推荐

