如何用Java 8泛型实现提取对象非空值的通用Map生成方法
这需求太实用了!我给你整理了几种靠谱的解决方案,从纯JDK实现到借助第三方库简化代码都有,你可以根据自己的项目情况挑合适的:
方案一:纯JDK反射实现(无第三方依赖)
如果不想引入额外依赖,直接用Java反射就能搞定核心逻辑——遍历对象的所有字段,判断值是否非空,把符合条件的键值对存入Map。这里要注意区分引用类型和基本类型的非空判断:引用类型只要不是null就算非空;基本类型则要判断是否不等于默认值(比如int的0、boolean的false等)。
import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class ObjectToMapConverter { public static Map<String, Object> getNonNullFieldsMap(Object obj) { Map<String, Object> nonNullMap = new HashMap<>(); if (obj == null) { return nonNullMap; } Class<?> clazz = obj.getClass(); // 获取对象所有声明的字段(包括私有) Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { try { // 开启私有字段访问权限 field.setAccessible(true); Object fieldValue = field.get(obj); // 判断值是否符合“非空”要求 if (isValueNonEmpty(fieldValue, field.getType())) { nonNullMap.put(field.getName(), fieldValue); } } catch (IllegalAccessException e) { // 实际项目里可以换成日志打印或者自定义异常 e.printStackTrace(); } } return nonNullMap; } // 辅助方法:判断值是否非空 private static boolean isValueNonEmpty(Object value, Class<?> fieldType) { // 处理基本类型 if (fieldType.isPrimitive()) { return switch (fieldType.getName()) { case "int" -> (int) value != 0; case "long" -> (long) value != 0L; case "boolean" -> (boolean) value; case "double" -> (double) value != 0.0d; case "float" -> (float) value != 0.0f; case "char" -> (char) value != '\u0000'; case "byte" -> (byte) value != 0; case "short" -> (short) value != 0; default -> false; }; } // 引用类型直接判断是否为null return value != null; } // 测试你的Foo类 public static void main(String[] args) { Foo foo = new Foo(); foo.setId(1); foo.setName("foo test"); // city未赋值,默认是null Map<String, Object> result = getNonNullFieldsMap(foo); System.out.println(result); // 输出 {id=1, name=foo test} } } class Foo { private int id; private String name; private String city; // 标准getter/setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
优缺点:完全无依赖,轻量可控;但反射性能略低,且需要自己处理所有类型的非空判断,代码量稍大。
方案二:借助Apache Commons BeanUtils简化代码
如果项目已经引入了Apache Commons工具包,用BeanMap可以省去手动反射的繁琐,它会自动帮我们封装对象的字段和值。
首先需要引入依赖(Maven为例):
<dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency>
然后实现方法:
import org.apache.commons.beanutils.BeanMap; import java.util.HashMap; import java.util.Map; public class BeanUtilsConverter { public static Map<String, Object> getNonNullFieldsMap(Object obj) { Map<String, Object> nonNullMap = new HashMap<>(); if (obj == null) { return nonNullMap; } BeanMap beanMap = new BeanMap(obj); for (Object key : beanMap.keySet()) { String fieldName = key.toString(); // 跳过默认的class属性 if ("class".equals(fieldName)) { continue; } Object fieldValue = beanMap.get(fieldName); Class<?> fieldType = beanMap.getType(fieldName); if (isValueNonEmpty(fieldValue, fieldType)) { nonNullMap.put(fieldName, fieldValue); } } return nonNullMap; } // 复用之前的isValueNonEmpty方法 private static boolean isValueNonEmpty(Object value, Class<?> fieldType) { if (fieldType.isPrimitive()) { return switch (fieldType.getName()) { case "int" -> (int) value != 0; case "long" -> (long) value != 0L; case "boolean" -> (boolean) value; case "double" -> (double) value != 0.0d; case "float" -> (float) value != 0.0f; case "char" -> (char) value != '\u0000'; case "byte" -> (byte) value != 0; case "short" -> (short) value != 0; default -> false; }; } return value != null; } // 测试 public static void main(String[] args) { Foo foo = new Foo(); foo.setId(1); foo.setName("foo test"); Map<String, Object> result = getNonNullFieldsMap(foo); System.out.println(result); // 输出 {id=1, name=foo test} } }
优缺点:代码更简洁,反射细节被封装;但需要引入第三方依赖,和纯反射方案性能差不多。
方案三:用Jackson实现(适合Spring等已依赖Jackson的项目)
如果你的项目已经在用Jackson(比如Spring Boot项目默认就有),可以直接用ObjectMapper把对象转成Map,再过滤掉空值,非常省心。
import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import java.util.stream.Collectors; public class JacksonConverter { // 全局复用ObjectMapper,避免重复创建 private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static Map<String, Object> getNonNullFieldsMap(Object obj) { if (obj == null) { return Map.of(); } try { // 先把对象转成包含所有字段的Map Map<String, Object> fullMap = OBJECT_MAPPER.convertValue(obj, Map.class); // 过滤掉空值(这里同样可以处理基本类型默认值) return fullMap.entrySet().stream() .filter(entry -> isValueNonEmpty(entry.getValue(), getFieldType(obj, entry.getKey()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } catch (Exception e) { e.printStackTrace(); return Map.of(); } } // 获取字段的原始类型 private static Class<?> getFieldType(Object obj, String fieldName) { try { return obj.getClass().getDeclaredField(fieldName).getType(); } catch (NoSuchFieldException e) { return Object.class; } } // 复用isValueNonEmpty方法 private static boolean isValueNonEmpty(Object value, Class<?> fieldType) { // Jackson会把基本类型转成包装类型,所以这里要处理包装类 if (fieldType.isPrimitive()) { return switch (fieldType.getName()) { case "int" -> (Integer) value != 0; case "long" -> (Long) value != 0L; case "boolean" -> (Boolean) value; case "double" -> (Double) value != 0.0d; case "float" -> (Float) value != 0.0f; case "char" -> (Character) value != '\u0000'; case "byte" -> (Byte) value != 0; case "short" -> (Short) value != 0; default -> false; }; } return value != null; } // 测试 public static void main(String[] args) { Foo foo = new Foo(); foo.setId(1); foo.setName("foo test"); Map<String, Object> result = getNonNullFieldsMap(foo); System.out.println(result); // 输出 {id=1, name=foo test} } }
优缺点:借助成熟的JSON库,代码简洁且稳定;如果项目已经依赖Jackson,几乎没有额外成本;缺点是如果没有依赖Jackson,需要引入它。
内容的提问来源于stack exchange,提问作者Shahid Ghafoor
相关产品推荐
相关产品推荐

