遍历可以是另一个对象的对象字段

问题描述

是否存在一种使用反射迭代对象字段的好方法? 主要问题在于对象可以是另一个对象,因此也需要迭代另一个对象的属性。

例如,我有AllInclusiveDetails对象

public class AllInclusiveDetails {
   
   @JsonProperty("renters_business")
   private String rentersBusiness;

   @Valid
   @NotNull
   @JsonProperty("main_property_owner_details")
   private ShortCustomer mainPropertyOwnerDetails;

   @Valid
   @NotNull
   @JsonProperty("main_tenant_details")
   private ShortCustomer mainTenantDetails;
}

ShortCustomer是

public class ShortCustomer {

  @NotNull
  @Positive
  private Long id;

  @NotEmpty
  @JsonProperty("full_name")
  private String fullName;

  @JsonProperty("organization_number")
  private String organizationNumber;

  @PastOrPresent
  private LocalDate birthdate;

}

我想使用反射来遍历AllInclusiveDetails对象字段,如果其中还有另一个对象,我也想遍历该对象字段。

主要目的是跟踪两个不同对象中相同字段的值是否相等,以及是否不保存旧值和新值。

解决方法

这是否满足您的要求:

for(Field field : AllInclusiveDetails.class.getDeclaredFields()) {
        if(field.getType()== ShortCustomer.class) {
            //Do your logic here
        }
    }
,

这是获取类的所有字段的一种方法,也是一种使用反射递归地比较字段的方法。用main进行测试,它在逻辑上等效但内存不相同的对象将无法正常工作。

// Gathers all fields of this class,including those in superclasses,regardless of visibility
public static List<Field> getAllFields(Class<?> klass) {
    List<Field> fields = new ArrayList<>();
    for (Class<?> k = klass; k != null; k = k.getSuperclass()) {
        fields.addAll(Arrays.asList(k.getDeclaredFields()));
    }
    return fields;
}

// Uses reflection and recursion to deep compare two objects.
// If the sub-fields and sub-arrays are not deeply equal this will return false.
// This will cause problems with data structures that may be logically equivalent
// but not have the same structure in memory,HashMaps and Sets come to mind
//
// Also might perform illegal reflective access which gets a warning from the JVM
// WARNING: Illegal reflective access ... to field java.util.LinkedList.size
// WARNING: Please consider reporting this to the maintainers ...
// WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
// WARNING: All illegal access operations will be denied in a future release
public static <T> boolean reflexiveEquals(T o1,T o2) {
    return reflexiveEquals(o1,o2,new HashSet<>(),new HashSet<>());
}
private static <T> boolean reflexiveEquals(T o1,T o2,Set<Object> o1Refs,Set<Object> o2Refs) {
    if (o1 == o2) {
        // exact same object or both are null
        return true;
    }
    if (o1 == null || o2 == null) {
        // one is null but the other is not
        System.err.println(o1 + " != " + o2);
        return false;
    }

    Class<?> type = o1.getClass();
    if (type != o2.getClass()) {
        // not the exact same class therefore not equal
        // you could treat this differently if you want
        System.err.println(type + " != " + o2.getClass());
        return false;
    }

    if (PRIMITIVE_WRAPPERS.contains(type)) {
        // if it's a primitive wrapper then compare plainly
        boolean result = Objects.equals(o1,o2);
        if (!result) {
            System.err.println("Objects.equals: " + o1 + " : " + o2);
        }
        return result;
    }

    // before descending,make sure there wont be an infinite loop
    // if this object appeared in the reference chain before
    // then it is currently being compared lower in the stack,// return true to let it finish it's comparison
    if (o1Refs.contains(o1) || o2Refs.contains(o2)) {
        return true;
    }

    try {
        // keep track of the objects that have been descended into
        o1Refs.add(o1);
        o2Refs.add(o2);

        if (type.isArray()) {
            // if its an array,compare all elements
            try {
                Object[] a1 = (Object[]) o1;
                Object[] a2 = (Object[]) o2;
                // only comparable field besides elements
                if (a1.length != a2.length) {
                    System.err.println("Array length diff");
                    return false;
                }
                for (int i = 0; i < a1.length; i++) {
                    if (!reflexiveEquals(a1[i],a2[i],o1Refs,o2Refs)) {
                        return false;
                    }
                }
                return true;
            } catch (Exception e) {
                return false;
            }
        }

        // otherwise its some other object so compare all fields
        // moving up the super-classes as well
        for (Class<?> k = type; k != null; k = k.getSuperclass()) {
            for (Field f : k.getDeclaredFields()) {
                try {
                    f.setAccessible(true);
                    if (!reflexiveEquals(f.get(o1),f.get(o2),o2Refs)) {
                        return false;
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    return false;
                }
            }
        }
        return true;
    } finally {
        // remove the references since their compare is complete
        o1Refs.remove(o1);
        o2Refs.remove(o2);
    }
}

private static final Set<Class<?>> PRIMITIVE_WRAPPERS = getPrimitiveWrapperClasses();
private static final Set<Class<?>> getPrimitiveWrapperClasses() {
    Set<Class<?>> set = new HashSet<>();
    set.add(Boolean.class);
    set.add(Character.class);
    set.add(Byte.class);
    set.add(Short.class);
    set.add(Integer.class);
    set.add(Long.class);
    set.add(Float.class);
    set.add(Double.class);
    set.add(Void.class);
    return set;
}

public static class AllInclusiveDetails {
    private String rentersBusiness;
    private ShortCustomer mainPropertyOwnerDetails;
    private ShortCustomer mainTenantDetails;
    private ShortCustomer[] arr;
    private List<ShortCustomer> list;
}

public static class ShortCustomer {
    private Long id;
    private String fullName;
    private String organizationNumber;
    private LocalDate birthdate;
}

public static void main(String[] args) {
    AllInclusiveDetails aids1 = new AllInclusiveDetails();
    aids1.rentersBusiness = "Business";
    aids1.mainTenantDetails = new ShortCustomer();
    aids1.mainTenantDetails.id = 1L;
    aids1.mainTenantDetails.fullName = "John Doe";
    aids1.arr = new ShortCustomer[] {
            aids1.mainTenantDetails,aids1.mainPropertyOwnerDetails };
    aids1.list = new LinkedList<>(Arrays.asList(aids1.arr));

    AllInclusiveDetails aids2 = new AllInclusiveDetails();
    aids2.rentersBusiness = "Business";
    aids2.mainTenantDetails = new ShortCustomer();
    aids2.mainTenantDetails.id = 1L;
    aids2.mainTenantDetails.fullName = "John Doe";
    aids2.arr = new ShortCustomer[] {
            aids2.mainTenantDetails,aids2.mainPropertyOwnerDetails };
    aids2.list = new LinkedList<>(Arrays.asList(aids2.arr));

    System.out.println(reflexiveEquals(aids1,aids2));
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...