问题描述
是否存在一种使用反射迭代对象字段的好方法? 主要问题在于对象可以是另一个对象,因此也需要迭代另一个对象的属性。
例如,我有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));
}