Java反射中如何通过Field对象正确获取字段对应的Class类型?
核心问题原因
你调用三个API失效的根本原因是对反射API的作用理解有误,逐个说明:
field.getClass():该方法是Object类自带的getClass(),返回的是Field对象自身的类型,也就是永远返回java.lang.reflect.Field,完全无法获取字段本身的声明类型,属于API误用。field.getType():这个API本身是正确的,它确实会返回字段声明时的类型Class对象。你之前用它遍历拿不到ClassB自定义字段,是两个原因导致:一是后续传参给递归方法时逻辑错误,二是getDeclaredFields()本身仅返回当前类直接声明的字段,不包含父类继承的字段,和getType()本身无关。field.get(xxx):该方法的作用是获取指定对象实例上该字段的实际存储值,要求传入的参数是持有该字段的目标对象实例,不能传入类型对象。你之前写f.get(f.getType()),相当于传入了一个Class类型的对象作为字段持有者,和字段所属的宿主类类型完全不匹配,因此抛出Can not set DataClass field ParentClass.testClass to java.lang.Class的类型不匹配错误。
正确实现方案
首先明确三个基础逻辑:
- 仅需要获取字段的声明类型时,直接调用
field.getType()即可拿到正确的Class对象 - 需要获取字段的实际值做递归遍历时,
field.get()必须传入当前正在处理的外层业务对象,不能传类型 - 访问非public字段前必须设置访问权限,要获取继承自父类的字段需要向上遍历父类
修正后的代码如下:
public static void load(Object obj) throws IllegalAccessException { // 空值直接返回,避免空指针 if (obj == null) { return; } Class<?> currentClazz = obj.getClass(); // 遍历当前类及其所有父类,直到Object类为止,解决拿不到父类字段的问题 while (currentClazz != null && currentClazz != Object.class) { for (Field f : currentClazz.getDeclaredFields()) { // 开放私有字段的访问权限 f.setAccessible(true); System.out.println("Found field: " + f.getName() + ", declared type: " + f.getType().getSimpleName()); // 获取当前对象上该字段的实际值,注意传入的是当前处理的业务对象obj Object fieldValue = f.get(obj); // 此处可自行加过滤规则,比如跳过基本类型、String、JDK内置类型,避免循环递归 if (fieldValue != null) { load(fieldValue); } } // 向上切换到父类继续遍历 currentClazz = currentClazz.getSuperclass(); } }
补充注意事项
- 不要用
getFields()替代getDeclaredFields(),前者仅能获取类的public字段,无法获取private、protected、默认访问权限的字段 - 递归时一定要加类型过滤和空值判断,避免遇到循环引用(比如A持有B、B持有A)时触发栈溢出
- 如果字段是泛型类型,
getType()只能拿到泛型的擦除后类型,要获取实际泛型参数需要调用getGenericType()方法解析
内容的提问来源于stack exchange,提问作者SvenWollinger
相关产品推荐
相关产品推荐

