You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:37:50