【问题标题】:How do I skip over null items in an object without writing multiple if statements or try blocks?如何在不编写多个 if 语句或 try 块的情况下跳过对象中的空项?
【发布时间】:2015-04-22 19:56:28
【问题描述】:

我正在使用一些代码,其中用户发送一个可以有超过 100 个值的对象。 (我们以汽车为例)。

updateDatabasePrep(carObject);

汽车对象可以包含名称、里程、VIN、颜色等...但是说我只设置颜色然后传递对象。

在我的方法中:

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);
}

它使用访问器方法从传递的对象中提取数据,然后尝试设置所述值。在上述情况下,由于里程和名称为空,会抛出空指针错误;只有颜色不是。

我的目标是仅使用已设置的值更新数据库。在这种情况下,我可以为每个单独的语句编写大量的 if/else 或 try/catch 语句,这是可行的,但我不希望这样做。

在 Java 中有没有更短的方法来做到这一点?有没有一种方法可以让您只设置非空数据,而不是 if/else、try/catch 以及遍历每一个 setter 方法并告诉它跳过空值?

编辑: 澄清一下,当它尝试设置空值时,会抛出 Nullpointerexception。浏览器

myObject.setMilage(x1);

因为我们问的人。

【问题讨论】:

  • 使用强类型语言,有时您应该弄脏自己的手。
  • updateDatabasePrep 方法的第一行不完整,“NewObject myObject...”。您能否更新示例代码以显示 myObject 是如何创建的?因为,除非您在 NewObject 类的 setter 方法中进行了一些特殊的错误检查,否则您不应该从设置 myObject 值的代码中得到任何错误。但是,如果您从未初始化 myObject,您将获得 NullPointerException。
  • 你必须使用反射来解决这个问题。
  • 好的,我得看看反射,我之前没用过
  • 好的,我看到你更新了示例代码,在 myObject 之后添加了一个分号,但是,这永远不会真正编译,因为你试图在 myObject 上设置一些东西,而它从来没有被初始化。不知道这与您的实际代码有多接近,但在调用 myObject 上的设置器之前的某个时刻,您必须为其分配一个新的 NewObject 实例,例如“NewObject myObject = new NewObject();”。我想你已经知道了?

标签: java object if-statement try-catch getter-setter


【解决方案1】:

假设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));
}

【讨论】:

    【解决方案2】:

    反思是你的答案。您也可以尝试使用一些更方便的库(例如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 语句样板花费更多的时间 - 如果你需要性能,你应该三思而后行。

    【讨论】:

    • 太棒了,谢谢。此外,在您的帖子脚本中提到它确实会减慢代码速度,我应该注意这一点。
    【解决方案3】:

    我不确定您是如何使用过程或准备好的调用来更新数据库的。不过,以下内容可能对您有所帮助。

    您可以获得具有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;
    }
    

    否则,您也可以使用类似的方法获取具有非空值的字段(也可以是精确值)。​​

    然后,您可以根据此结果动态构造您的准备好的语句。祝你好运!

    【讨论】:

      【解决方案4】:

      仅适用于 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;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-29
        • 2019-03-06
        • 2010-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多