如何将全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
相关产品推荐
相关产品推荐

