【问题标题】:Get all fields (even private and inherited) from class从类中获取所有字段(甚至是私有的和继承的)
【发布时间】:2013-04-24 03:22:28
【问题描述】:

我正在做大学项目。
我需要从课堂上获取所有字段。甚至私人和继承。我试图获取所有声明的字段,然后转换为超类并重复。 我的代码片段:

private void listAllFields(Object obj) {
    List<Field> fieldList = new ArrayList<Field>();
    while (obj != null) {
        fieldList.addAll(Arrays.asList(obj.getClass().getDeclaredFields()));
        obj = obj.getClass().getSuperclass().cast(obj);
    }
    // rest of code

但它不起作用。 tmpObj 转换后仍然是同一个类(不是超类)。
我将不胜感激如何解决投射问题或如何以不同方式检索这些字段。

问题不是获取字段的访问权限,而是获取字段的名称!
我就是这样管理的:

private void listAllFields(Object obj) {
    List<Field> fieldList = new ArrayList<Field>();
    Class tmpClass = obj.getClass();
    while (tmpClass != null) {
        fieldList.addAll(Arrays.asList(tmpClass .getDeclaredFields()));
        tmpClass = tmpClass .getSuperclass();
    }
    // rest of code

【问题讨论】:

标签: java reflection


【解决方案1】:

在提取值之前,您必须将 setAccessible 设置为 true。有时JVM 不允许提取私有变量,它可能会给出安全异常。

ClassName obj  =  new ClassName();
Field field  =   ClassName.class.getDeclaredField  ("name of the field");
field.setAccessible   (true);
DataType value  =  (DataType) field.get  (obj);

【讨论】:

  • getDeclaredField 不会获取继承的成员。上面提到的关于遍历所有超类的答案是确切的方法。这只会列出同一类的成员变量。
【解决方案2】:

这里有一个解决方案

//我的对象

    Student student =new Student();

    Field[] field2=student.getClass().getSuperclass().getDeclaredFields();
    Field[] field1=student.getClass().getDeclaredFields();
    
    Field [] all_Fields=new Field[field1.length+field2.length];
    
    
    //Joining field 1 and two
      System.arraycopy(field2, 0, all_Fields, 0, field2.length);
      System.arraycopy(field1, 0, all_Fields, field2.length, field1.length);
    

      //Printing all fields
    for(Field i:all_Fields)
    {
        System.out.println(i);
    }

//如果你想从多个超类中获取字段那么你可以使用下面的代码进行迭代:

    Class clazz = Student.class;
    while (clazz != null) {
      clazz= clazz.getSuperclass(); 
    }

【讨论】:

  • 如果一个类有多个继承级别,这不起作用
  • 如果超类继承了另一个类,您仍然可以通过迭代超类来获取超类的继承类字段。这是代码: Class clazz = Student.class; while (clazz != null) { System.out.println(clazz.getName()); clazz= clazz.getSuperclass(); }
  • 我知道,但复制您的示例代码的人可能不会。由于原始问题要求所有继承的字段,您的解决方案目前无法回答该问题。也许您可以稍微编辑一下代码以反映这一点。
【解决方案3】:
obj = obj.getClass().getSuperclass().cast(obj);

这条线没有做你期望它做的事情。强制转换 Object 实际上并没有改变它,它只是告诉编译器将其视为其他东西。

例如您可以将List 转换为Collection,但仍将是List

但是,通过超类循环访问字段可以正常工作而无需强制转换:

Class<?> current = yourClass;
while(current.getSuperclass()!=null){ // we don't want to process Object.class
    // do something with current's fields
    current = current.getSuperclass();
}

顺便说一句,如果您可以访问 Spring 框架,有一个方便的方法可以循环遍历类和所有超类的字段:
ReflectionUtils.doWithFields(baseClass, FieldCallback)
(另见我之前的回答:Access to private inherited fields via reflection in Java

【讨论】:

  • 对我不起作用,我想从超类中获取字段值,在这里我得到 Class 对象。
【解决方案4】:

此解决方案使用 Java 8 流,对学习 Java 函数式编程的人很有用。它根据其他答案迭代 getSuperclass 和 getDeclaredFields ,但它以功能方式进行。

以下行将打印在 SomeClass 或其任何超类中找到的每个字段名称的名称。

allFieldsFor(SomeClass.class).map(Field::getName).forEach(System.out::println);

这里是通过超类逐步创建字段流的代码。

private Stream<Field> allFieldsFor( Class c ) {
    return walkInheritanceTreeFor(c).flatMap( k -> Arrays.stream(k.getDeclaredFields()) );
}

private Stream<Class> walkInheritanceTreeFor( Class c ) {
    return iterate( c, k -> Optional.ofNullable(k.getSuperclass()) );
}

以下方法是从 Streams.iterate 建模的,但是 Streams.iterate 旨在创建无限流。此版本已修改为在 fetchNextFunction 返回 Optional.none() 时结束。

private <T> Stream<T> iterate( T seed, Function<T,Optional<T>> fetchNextFunction ) {
    Objects.requireNonNull(fetchNextFunction);

    Iterator<T> iterator = new Iterator<T>() {
        private Optional<T> t = Optional.ofNullable(seed);

        public boolean hasNext() {
            return t.isPresent();
        }

        public T next() {
            T v = t.get();

            t = fetchNextFunction.apply(v);

            return v;
        }
    };

    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize( iterator, Spliterator.ORDERED | Spliterator.IMMUTABLE),
        false
    );
}

【讨论】:

    【解决方案5】:

    这是我用来获取对象所有字段的方法

    private <T> List<Field> getFields(T t) {
            List<Field> fields = new ArrayList<>();
            Class clazz = t.getClass();
            while (clazz != Object.class) {
                fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
                clazz = clazz.getSuperclass();
            }
            return fields;
        }
    

    【讨论】:

      【解决方案6】:

      getDeclaredFields() 为您提供该类的所有字段,包括 private 的字段。

      getFields() 为您提供该类及其超类上的所有 public 字段。

      如果你想要超类的private/protected方法,你将不得不反复调用getSuperclass(),然后在超类对象上调用getDeclaredFields()

      javadocs 没有明确解释这里的内容

      【讨论】:

        【解决方案7】:

        试试这些

        How do I read a private field in Java?

        Is it possible in Java to access private fields via reflection

        Field.setAccessible(true) 是你必须做的才能通过反射访问它

        import java.lang.reflect.Field;
        
        class B 
        {
            private int i = 5;
        }
        
        public class A extends B 
        {
            public static void main(String[] args) throws Exception 
            {
                A a = new A();
                Field[] fs = a.getClass().getSuperclass().getDeclaredFields();
                for (Field field : fs)
                {
                    field.setAccessible(true);
                    System.out.println( field.get( a ) );
                }
            }
        }
        

        【讨论】:

        • 我不知道字段的名称 - 这必须是通用解决方案。我知道我可以使字段可访问,但首先我必须知道父私有字段的名称。我的问题是获得该名称,而不是在我拥有它时访问它。
        • 对于私有字段,您需要知道名称和继承使用 getSuperclass() 方法
        【解决方案8】:

        以前问过。

        Access to private inherited fields via reflection in Java

        在您的代码中,设置field.setAccessible(true);

        【讨论】:

          【解决方案9】:

          要获取超类字段,请使用getSuperclass()。从那里你可以得到超类的字段。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-04-26
            • 1970-01-01
            • 1970-01-01
            • 2013-01-09
            • 2020-10-25
            • 1970-01-01
            相关资源
            最近更新 更多