代码是常规的,但答案可以是Groovy或Java。 我有一个包含此字段的Person类:
class Person(){
String name
String lasName
}
我有一个从同一个类返回两个对象的方法。一个对象包含一些字段,另一个包含其余字段,在我的示例中它将是这样的:
person1 = "name : Jon"
person2 = "lastName : Snow"
我需要的是将person1
的所有空字段替换为person2
字段,如果这不是null,在我们的示例中,输出将为:
person1.merge(person2)
person1= "name : Jon, lastName : Snow"
Java或Groovy上是否有任何方法可以在不编写所有字段(使用某种循环)的情况下执行类似的操作?
如果没有任何默认方法可供使用,我如何迭代一个类中的所有字段?
答案 0 :(得分:2)
刚刚使用反射测试。所需的输出是
merged person:Person{name=John, lastName=Snow}
public static void testReflection() {
Person p1 = new Person("John", null);
Person p2 = new Person(null, "Snow");
Person merged = (Person) mergePersons(p1, p2);
System.out.println("merged person:" + merged);
}
public static Object mergePersons(Object obj1, Object obj2) throws Exception {
Field[] allFields = obj1.getClass().getDeclaredFields();
for (Field field : allFields) {
if (Modifier.isPublic(field.getModifiers()) && field.isAccessible() && field.get(obj1) == null && field.get(obj2) != null) {
field.set(obj1, field.get(obj2));
}
}
return obj1;
}
mergePersons
接受两个对象。
然后它遍历所有字段并验证第一个对象是否具有空值。 如果是,则验证第二个对象是否未被清零。
如果这是真的,则将值分配给第一个Object。
提供此解决方案只能访问公共数据。如果您还想访问私人数据,则需要删除修改器验证并设置是否可访问,如下所示:
public static Object mergePersons(Object obj1, Object obj2) throws Exception {
Field[] allFields = obj1.getClass().getDeclaredFields();
for (Field field : allFields) {
if (!field.isAccessible() && Modifier.isPrivate(field.getModifiers()))
field.setAccessible(true);
if (field.get(obj1) == null && field.get(obj2) != null) {
field.set(obj1, field.get(obj2));
}
}
return obj1;
}
答案 1 :(得分:2)
鉴于Groovy字段是作为带有支持字段的getter / setter对实现的,你可以在Groovy中这样做:
static <T> void merge(T from, T to) {
from.metaClass.properties.findAll { p ->
p.getProperty(to) == null &&
p.getProperty(from) != null &&
to.respondsTo(MetaProperty.getSetterName(p.name))
}
.each {
p -> p.setProperty(to, p.getProperty(from))
}
}
答案 2 :(得分:1)
你必须走反射路线。我假设您有一个默认构造函数,否则以下内容无法正常工作。此外,它需要两种相同的类型。
public static <T> T mergeObjects(T first, T second) throws IllegalAccessException, InstantiationException {
Class<?> clazz = first.getClass();
Field[] fields = clazz.getDeclaredFields();
Object returnValue = clazz.newInstance();
for (Field field : fields) {
field.setAccessible(true);
Object value1 = field.get(first);
Object value2 = field.get(second);
Object value = (value1 != null) ? value1 : value2;
field.set(returnValue, value);
}
return (T) returnValue;
}
这是示例
import java.lang.reflect.Field;
public class Merge2Obj {
private String name;
private String lasName;
public Merge2Obj() {
super();
}
public Merge2Obj(String name, String lasName) {
super();
this.name = name;
this.lasName = lasName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLasName() {
return lasName;
}
public void setLasName(String lasName) {
this.lasName = lasName;
}
public static <T> T mergeObjects(T first, T second) throws IllegalAccessException, InstantiationException {
Class<?> clazz = first.getClass();
Field[] fields = clazz.getDeclaredFields();
Object returnValue = clazz.newInstance();
for (Field field : fields) {
field.setAccessible(true);
Object value1 = field.get(first);
Object value2 = field.get(second);
Object value = (value1 != null) ? value1 : value2;
field.set(returnValue, value);
}
return (T) returnValue;
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Merge2Obj obj1 = new Merge2Obj("ABC", null);
Merge2Obj obj2 = new Merge2Obj("PQR", "LMN");
Merge2Obj obj3 = mergeObjects(obj1, obj2);
System.out.println(obj3.name);
System.out.println(obj3.lasName);
}
}
答案 3 :(得分:1)
这是一种快速(且冒昧)的方法,与使用字段上的反射基本相同,但使用:
getProperties()
method,它为我们提供了一个属性名称和值的Map 鉴于这两个功能,您可以将要合并的每个对象描述为其属性的地图,去掉null
- 值条目,将地图组合在一起(并删除讨厌的“类”条目,是readonly),并使用合并的Map来构建合并的实例。
class Person {
String first, last, middle
}
def p1 = new Person(first: 'bob')
def p2 = new Person(last: 'barker')
Person merged = (p1.properties.findAll { k, v -> v } // p1's non-null properties
+ p2.properties.findAll { k, v -> v }) // plus p2's non-null properties
.findAll { k, v -> k != 'class' } // excluding the 'class' property
assert merged.first == 'bob'
assert merged.last == 'barker'
assert merged.middle == null
答案 4 :(得分:1)
假设有一个带有getter和setter的可变数据类,Apache BeanUtils可能适合您的需求。
默认情况下,BeanUtilBeansBean.copyProperties(Object dest, Object orig)
会查找T orig.get*()
和dest.set*(T value)
对,并使用前者的结果调用后者。
但是您可以注入自定义PropertyUtilsBean
,因此您可以包装默认值以防止它替换非空属性:
public NoClobberPropertyUtilsBean extends PropertyUtilsBean {
@Override
public void setSimpleProperty((Object bean,
String name,
Object value)
throws IllegalAccessException,
InvocationTargetException,
NoSuchMethodException {
if(getProperty(bean,name) == null) {
super.setSimpleProperty(bean,name,value);
}
}
}
现在您可以合并:
BeanUtilsBean beanUtils = new BeanUtilsBean(new ConvertUtilsBean(), new NoClobberPropertyUtilsBean());
Person merged = new Person();
beanUtils.copyProperties(person1);
beanUtils.copyProperties(person2);
如果两个来源中的属性都为非空,则第一个copyProperties
获胜。
你当然可以改变语义,例如,如果守卫是if(value != null)
,它会表现得不同。
在某个级别,BeanUtils只是其他答案所暗示的反射操作的包装器。是否需要额外的抽象级别取决于您。如果要支持map / list成员或BeanUtils'DynaBean
类,可能需要覆盖更多方法。