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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:18:04