如何在不编写多个 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:请注意,使用此方法会对您的 类(CarNewObject)施加额外的限制 - 它们应该是 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;
    }