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

如何将全Optional字段的类转换为非Optional字段的类

问题:将全Optional字段的类转换为全非Optional类

我有一个包含大量字段的类,从JSON反序列化时所有字段均为Optional类型,希望将其转换为一个所有字段均为非Optional类型的匹配类,且已知所有Optional字段均已赋值。

可选类定义:

@JsonDeserialize
public class VeryOptionalClass
{
    @Value.Parameter
    @JsonProperty("fieldOne")
    public Optional<String> fieldOne();

    @Value.Parameter
    @JsonProperty("fieldTwo")
    public Optional<Double> fieldTwo();

    @Value.Parameter
    @JsonProperty("fieldThree")
    public Optional<String> fieldThree();

    @Value.Parameter
    @JsonProperty("fieldFour")
    public Optional<SomeObject> fieldFour();
 
    // 省略更多字段...

    @Value.Parameter
    @JsonProperty("fieldHundredTwentyFive")
    public Optional<String> fieldHundredTwentyFive();
}

期望的非可选类:

public class TotallyNonOptionalClass
{
    public String fieldOne();
    public Double fieldTwo();
    public String fieldThree();
    public SomeObject fieldFour();

    // 省略更多字段...

    public String fieldHundredTwentyFive();
}

我知道可以用Builder模式手动转换,但字段太多,不想每次新增字段都同步修改两个类的转换代码,希望有更优雅的实现方式。


解决方案

1. 用MapStruct自动生成映射代码(推荐)

MapStruct是编译时生成映射逻辑的工具,能自动处理Optional到非Optional的转换,且字段新增时无需手动修改映射代码,只要两个类的字段名、类型匹配即可。

步骤1:定义映射接口

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

@Mapper
public interface ClassMapper {
    ClassMapper INSTANCE = Mappers.getMapper(ClassMapper.class);
    
    TotallyNonOptionalClass toNonOptional(VeryOptionalClass source);
}

步骤2:使用映射

VeryOptionalClass optionalObj = someProvider(someInput);
TotallyNonOptionalClass nonOptionalObj = ClassMapper.INSTANCE.toNonOptional(optionalObj);

MapStruct会自动生成编译期代码,将每个Optional字段调用.get()(因为已知非空)赋值给目标类的对应字段,同时提供编译期字段匹配检查,避免运行时错误。

2. 用ModelMapper实现运行时映射

ModelMapper是运行时映射工具,只需配置一次转换规则即可通用处理:

import org.modelmapper.ModelMapper;
import org.modelmapper.TypeMap;

public class ClassConverter {
    private static final ModelMapper modelMapper = new ModelMapper();
    
    static {
        // 配置Optional到对应非可选类型的转换,已知非空直接抛出异常兜底
        TypeMap<Optional<?>, Object> optionalConverter = modelMapper.createTypeMap(Optional.class, Object.class);
        optionalConverter.setConverter(ctx -> ctx.getSource().orElseThrow(() -> new IllegalStateException("Optional字段为空")));
    }
    
    public static TotallyNonOptionalClass convert(VeryOptionalClass source) {
        return modelMapper.map(source, TotallyNonOptionalClass.class);
    }
}

使用时直接调用:

TotallyNonOptionalClass nonOptionalObj = ClassConverter.convert(optionalObj);

优点是无需编写映射接口,缺点是运行时处理性能略低于MapStruct,字段不匹配问题需运行时才能发现。

3. 反射实现通用转换(无第三方依赖)

如果不想引入外部库,可以用反射写一个通用转换工具,遍历源类方法获取Optional值,自动调用目标类的Builder赋值:

import java.lang.reflect.Method;
import java.util.Optional;

public class OptionalConverter {
    @SuppressWarnings("unchecked")
    public static <T, U> U convert(T source, Class<U> targetClass) {
        try {
            // 假设目标类使用Lombok @Builder
            Object builder = targetClass.getMethod("builder").invoke(null);
            
            for (Method sourceMethod : source.getClass().getDeclaredMethods()) {
                if (Optional.class.isAssignableFrom(sourceMethod.getReturnType())) {
                    String fieldName = sourceMethod.getName();
                    // 获取目标Builder的对应设置方法
                    Method setter = builder.getClass().getMethod(fieldName, 
                        sourceMethod.getReturnType().getTypeParameters()[0].getRawType());
                    // 已知非空直接get
                    setter.invoke(builder, ((Optional<?>) sourceMethod.invoke(source)).get());
                }
            }
            
            return (U) builder.getClass().getMethod("build").invoke(null);
        } catch (Exception e) {
            throw new RuntimeException("转换失败", e);
        }
    }
}

使用示例:

TotallyNonOptionalClass nonOptionalObj = OptionalConverter.convert(optionalObj, TotallyNonOptionalClass.class);

该方案无需依赖,但反射性能一般,且要求源类和目标类的字段名完全一致,适合临时场景或字段稳定的情况。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 09:47:24