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

如何让MapStruct自动映射枚举值至伪枚举类常量?

如何用MapStruct自动实现枚举与伪枚举类常量的双向映射

问题背景

代码库中存在大量如下结构的伪枚举类:

public final class Target extends PseudoEnum {

  public static final Target FOO = new Target(1);
  public static final Target BAR = new Target(2);

  private Target(int value) {
    super(value);
  }
}

需要与标准枚举Source做双向映射:

public enum Source {
  FOO, BAR
}

直接使用MapStruct的@ValueMapping会失败(因为Target不是标准枚举),手动维护映射Map又过于繁琐,希望能实现自动映射。

可行解决方案

方案一:通用反射转换器(推荐)

利用反射根据常量名称匹配的约定,编写通用转换器,一次性解决所有同结构伪枚举的映射问题,无需为每个伪枚举编写重复代码。

1. 编写通用转换器类

import java.lang.reflect.Field;
import java.util.Arrays;

public class PseudoEnumConverter {

    // 标准枚举转伪枚举
    @SuppressWarnings("unchecked")
    public static <T extends PseudoEnum> T enumToPseudoEnum(Enum<?> source, Class<T> targetClass) {
        if (source == null) {
            return null;
        }
        try {
            // 通过枚举名称匹配伪枚举的静态常量
            Field field = targetClass.getField(source.name());
            return (T) field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new IllegalArgumentException("无法匹配对应伪枚举常量: " + source.name(), e);
        }
    }

    // 伪枚举转标准枚举
    public static <E extends Enum<E>> E pseudoEnumToEnum(PseudoEnum source, Class<E> enumClass) {
        if (source == null) {
            return null;
        }
        try {
            // 遍历伪枚举的静态常量,找到匹配实例后返回对应枚举
            Field matchingField = Arrays.stream(source.getClass().getFields())
                    .filter(field -> {
                        try {
                            return field.get(null).equals(source);
                        } catch (IllegalAccessException e) {
                            return false;
                        }
                    })
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("无法找到对应枚举值: " + source));
            return Enum.valueOf(enumClass, matchingField.getName());
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("无法匹配对应枚举值", e);
        }
    }
}

2. 在MapStruct Mapper中使用转换器

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface SourceMapper {

    SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class);

    default Target map(Source source) {
        return PseudoEnumConverter.enumToPseudoEnum(source, Target.class);
    }

    default Source map(Target target) {
        return PseudoEnumConverter.pseudoEnumToEnum(target, Source.class);
    }
}

这种方式的优势是:只要伪枚举的静态常量名与枚举名完全一致,新增常量时无需修改映射代码,完全遵循约定即可自动映射,适合大量伪枚举的场景。

方案二:半自动映射(无反射)

如果不希望使用反射,可以结合MapStruct的@ValueMapping注解和自定义方法,让映射代码更符合MapStruct的风格,缺点是新增常量时需要手动更新映射关系:

import org.mapstruct.Mapper;
import org.mapstruct.ValueMapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface SourceMapper {

    SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class);

    @ValueMapping(source = "FOO", target = "FOO")
    @ValueMapping(source = "BAR", target = "BAR")
    default Target map(Source source) {
        return switch(source) {
            case FOO -> Target.FOO;
            case BAR -> Target.BAR;
        };
    }

    @ValueMapping(source = "FOO", target = "FOO")
    @ValueMapping(source = "BAR", target = "BAR")
    default Source map(Target target) {
        if (target == Target.FOO) return Source.FOO;
        else if (target == Target.BAR) return Source.BAR;
        throw new IllegalArgumentException("未知伪枚举值: " + target);
    }
}

关于MapStruct官方支持

MapStruct目前没有原生支持类静态常量与枚举的自动映射,因为伪枚举本质是普通类的静态实例,而非JDK标准枚举类型,因此需要通过自定义逻辑来实现自动化映射。

内容的提问来源于stack exchange,提问作者Stov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:50:02