如何在不编写多个 if 语句或 try 块的情况下跳过对象中的空项?
How do I skip over null items in an object without writing multiple if statements or try blocks?
我正在处理一些代码,用户在其中发送一个对象,该对象可以有超过 100 个值。 (我们以汽车为例)。
updateDatabasePrep(carObject);
汽车对象可以有名称、里程数、车辆识别号、颜色等...但假设我只设置颜色然后传递对象。
在我的方法中:
public void updateDatabasePrep(Car carObject){
NewObject myObject = newObject(); //Initialized, but empty
String x1 = carObject.getMilage();
String x2 = carObject.getColor();
String x3 = carObject.getName();
//These will hold null values no problem
//Once the rest of the data has been collected:
//These, however, will error out when I try to set the null values.
myObject.setMilage(x1);
myObject.setColor(x2);
myObject.setName(x3);
}
它使用访问器方法从传递的对象中提取数据,然后尝试设置所述值。在上面的情况下,它会抛出一个空指针错误,因为 milage 和 name 都是空的;只有颜色不是。
我的目标是仅使用已设置的值更新数据库。在这种情况下,我可以为每个语句编写大量的 if/else 或 try/catch 语句,这会起作用,但我不想这样做。
在 Java 中有更短的方法吗?除了 if/else try/catch 之外,是否有一种方法只能在数据不为空时设置数据,然后遍历每个 setter 方法并告诉它跳过空值?
编辑:
澄清一下,当它试图设置一个空值时,会抛出 Nullpointerexception。即
myObject.setMilage(x1);
因为我们问的人。
我不确定您是如何更新数据库的,是使用过程还是准备调用。不过,以下内容可能对您有所帮助。
您可以获得具有 null
值的字段列表 -
public List<String> getNullFields(Car car) {
Field[] fields = car.getClass().getDeclaredFields();
List<String> result = new ArrayList<String>();
for (Field field : fields) {
try {
field.setAccessible(true);
Object value = field.get(car);
if(value == null) {
result.add(field.getName());
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
否则,您也可以使用类似的方法获得不具有空值的字段(以及精确值)。
然后,您可以根据这个结果动态地构造您的预处理语句。祝你好运!
假设 myObject
总是相同的类型,你可以使用反射来做一个映射阶段,把 getter 和 setter 方法放在一个 hashmap 中,就像这样:
private HashMap<Method, Method> methodMap;
private void doReflection()
{
Method[] carMethods = carObject.getClass().getDeclaredMethods();
ArrayList<Method> getters = new ArrayList<>();
for(int i = 0; i < carMethods.size(); i++)
if(carMethods[i].getName().startsWith("get")) getters.add(carMethods[i]);
methodMap = new HashMap<>();
for(Method m : getters)
{
String fieldName = m.getName().substring(3);
Method setter = myObject.class.getMethod("set" + fieldName, m.getParameterTypes());
if(setter != null) methodMap.put(m, setter);
}
}
现在您可以像这样迭代 HashMap 来进行赋值:
for(MapEntry<Method, Method> entry : methodMap.entrySet())
{
Method getter = entry.getKey();
Method setter = entry.getValue();
Object o = getter.invoke(carObject, null);
if(o != null) setter.invoke(myObject, getter.invoke(carObject, null));
}
反思就是你的答案。您也可以尝试使用一些库使其更方便(例如 commons-beanutils)。
所以你可以这样做:
private void copyIfSpecified(final Car from, final NewObject to, final String propName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
final Object value = PropertyUtils.getSimpleProperty(from, propName);
if (value != null) {
PropertyUtils.setSimpleProperty(to, propName, value);
}
}
并从您的方法中调用它:
public void updateDatabasePrep(Car carObject){
NewObject myObject = new NewObject(); //Initialized, but empty
try {
copyIfSpecified(carObject, myObject, "milage");
copyIfSpecified(carObject, myObject, "color");
copyIfSpecified(carObject, myObject, "name");
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException("Error while populating fields", ex);
}
}
通过这种方式,您可以明确指定要复制的属性,但可以避免过多的 if 语句。如果您不必确保始终复制您想要的所有字段(例如,字段集是固定的并且不会随着时间的推移而改变),您可以通过反射完成整个复制操作(即获取所有字段源对象并将非空值复制到目标对象的字段 - 请参阅 PropertyUtils.copyProperties(..)
实现)。
ps:请注意,使用此方法会对您的 类(Car
和 NewObject
)施加额外的限制 - 它们应该是 java bean:见 What is a JavaBean exactly?
pps: 还要注意,使用反射比普通的 if 语句样板文件要花费更多的时间——如果你需要性能,你应该三思而后行。
仅针对 getter 和 setter 的工作和优化代码(包括布尔类型的 "is")
public void copyCabInfo(CabInfo pCabInfo){
for(Map.Entry<Method, Method> entry : doReflection().entrySet())
{
Method getter = entry.getKey();
Method setter = entry.getValue();
Object o=null;
try {
o = getter.invoke(pCabInfo, null);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
}
if(o != null)
try {
setter.invoke(this, getter.invoke(pCabInfo, null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private static HashMap<Method, Method> methodMap;
private static HashMap<Method, Method> doReflection()
{
if(methodMap!=null && !methodMap.isEmpty())
return methodMap;
Method[] carMethods = CabInfo.class.getDeclaredMethods();
ArrayList<Method> getters = new ArrayList<>();
for(int i = 0; i < carMethods.length; i++)
if(carMethods[i].getName().startsWith("get") || carMethods[i].getName().startsWith("is") ) getters.add(carMethods[i]);
methodMap = new HashMap<>();
for(Method m : getters)
{
String methodName=m.getName();
String fieldName = methodName.startsWith("is")?methodName.substring(2):methodName.substring(3);
Method setter=null;
try {
setter = CabInfo.class.getMethod("set" + fieldName, m.getReturnType());
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(setter != null) methodMap.put(m, setter);
}
return methodMap;
}
我正在处理一些代码,用户在其中发送一个对象,该对象可以有超过 100 个值。 (我们以汽车为例)。
updateDatabasePrep(carObject);
汽车对象可以有名称、里程数、车辆识别号、颜色等...但假设我只设置颜色然后传递对象。
在我的方法中:
public void updateDatabasePrep(Car carObject){
NewObject myObject = newObject(); //Initialized, but empty
String x1 = carObject.getMilage();
String x2 = carObject.getColor();
String x3 = carObject.getName();
//These will hold null values no problem
//Once the rest of the data has been collected:
//These, however, will error out when I try to set the null values.
myObject.setMilage(x1);
myObject.setColor(x2);
myObject.setName(x3);
}
它使用访问器方法从传递的对象中提取数据,然后尝试设置所述值。在上面的情况下,它会抛出一个空指针错误,因为 milage 和 name 都是空的;只有颜色不是。
我的目标是仅使用已设置的值更新数据库。在这种情况下,我可以为每个语句编写大量的 if/else 或 try/catch 语句,这会起作用,但我不想这样做。
在 Java 中有更短的方法吗?除了 if/else try/catch 之外,是否有一种方法只能在数据不为空时设置数据,然后遍历每个 setter 方法并告诉它跳过空值?
编辑: 澄清一下,当它试图设置一个空值时,会抛出 Nullpointerexception。即
myObject.setMilage(x1);
因为我们问的人。
我不确定您是如何更新数据库的,是使用过程还是准备调用。不过,以下内容可能对您有所帮助。
您可以获得具有 null
值的字段列表 -
public List<String> getNullFields(Car car) {
Field[] fields = car.getClass().getDeclaredFields();
List<String> result = new ArrayList<String>();
for (Field field : fields) {
try {
field.setAccessible(true);
Object value = field.get(car);
if(value == null) {
result.add(field.getName());
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
否则,您也可以使用类似的方法获得不具有空值的字段(以及精确值)。
然后,您可以根据这个结果动态地构造您的预处理语句。祝你好运!
假设 myObject
总是相同的类型,你可以使用反射来做一个映射阶段,把 getter 和 setter 方法放在一个 hashmap 中,就像这样:
private HashMap<Method, Method> methodMap;
private void doReflection()
{
Method[] carMethods = carObject.getClass().getDeclaredMethods();
ArrayList<Method> getters = new ArrayList<>();
for(int i = 0; i < carMethods.size(); i++)
if(carMethods[i].getName().startsWith("get")) getters.add(carMethods[i]);
methodMap = new HashMap<>();
for(Method m : getters)
{
String fieldName = m.getName().substring(3);
Method setter = myObject.class.getMethod("set" + fieldName, m.getParameterTypes());
if(setter != null) methodMap.put(m, setter);
}
}
现在您可以像这样迭代 HashMap 来进行赋值:
for(MapEntry<Method, Method> entry : methodMap.entrySet())
{
Method getter = entry.getKey();
Method setter = entry.getValue();
Object o = getter.invoke(carObject, null);
if(o != null) setter.invoke(myObject, getter.invoke(carObject, null));
}
反思就是你的答案。您也可以尝试使用一些库使其更方便(例如 commons-beanutils)。
所以你可以这样做:
private void copyIfSpecified(final Car from, final NewObject to, final String propName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
final Object value = PropertyUtils.getSimpleProperty(from, propName);
if (value != null) {
PropertyUtils.setSimpleProperty(to, propName, value);
}
}
并从您的方法中调用它:
public void updateDatabasePrep(Car carObject){
NewObject myObject = new NewObject(); //Initialized, but empty
try {
copyIfSpecified(carObject, myObject, "milage");
copyIfSpecified(carObject, myObject, "color");
copyIfSpecified(carObject, myObject, "name");
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException("Error while populating fields", ex);
}
}
通过这种方式,您可以明确指定要复制的属性,但可以避免过多的 if 语句。如果您不必确保始终复制您想要的所有字段(例如,字段集是固定的并且不会随着时间的推移而改变),您可以通过反射完成整个复制操作(即获取所有字段源对象并将非空值复制到目标对象的字段 - 请参阅 PropertyUtils.copyProperties(..)
实现)。
ps:请注意,使用此方法会对您的 类(Car
和 NewObject
)施加额外的限制 - 它们应该是 java bean:见 What is a JavaBean exactly?
pps: 还要注意,使用反射比普通的 if 语句样板文件要花费更多的时间——如果你需要性能,你应该三思而后行。
仅针对 getter 和 setter 的工作和优化代码(包括布尔类型的 "is")
public void copyCabInfo(CabInfo pCabInfo){
for(Map.Entry<Method, Method> entry : doReflection().entrySet())
{
Method getter = entry.getKey();
Method setter = entry.getValue();
Object o=null;
try {
o = getter.invoke(pCabInfo, null);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
}
if(o != null)
try {
setter.invoke(this, getter.invoke(pCabInfo, null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private static HashMap<Method, Method> methodMap;
private static HashMap<Method, Method> doReflection()
{
if(methodMap!=null && !methodMap.isEmpty())
return methodMap;
Method[] carMethods = CabInfo.class.getDeclaredMethods();
ArrayList<Method> getters = new ArrayList<>();
for(int i = 0; i < carMethods.length; i++)
if(carMethods[i].getName().startsWith("get") || carMethods[i].getName().startsWith("is") ) getters.add(carMethods[i]);
methodMap = new HashMap<>();
for(Method m : getters)
{
String methodName=m.getName();
String fieldName = methodName.startsWith("is")?methodName.substring(2):methodName.substring(3);
Method setter=null;
try {
setter = CabInfo.class.getMethod("set" + fieldName, m.getReturnType());
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(setter != null) methodMap.put(m, setter);
}
return methodMap;
}