用于防御性编码的Wil Shipley的`IsEmpty`方法的Java实现

时间:2013-07-09 17:00:06

标签: java objective-c

我是一名Objective-C人,开始从事小型个人Java项目。在我的Objective-C项目中,我练习防御性编码,并在使用前定期使用Wil Shipley's IsEmpty(id thing) method检查空值或空值:

static inline BOOL IsEmpty (id thing)
{
    return thing == nil
        || ([thing respondsToSelector:@selector(length)] && [(NSData *)thing length] == 0)
        || ([thing respondsToSelector:@selector(count)] && [(NSArray *)thing count] == 0);
}

我知道Apache Commons做了这样的事情(虽然,它看起来只适用于String对象),我尝试用Java实现类似的东西,但找不到一种方法来调用当编译时类未知时,对象上的length()size()方法。

我是否试图将方形钉固定在圆孔中?如果没有,经验丰富的Java开发人员如何实现通用检查以确保各种对象不为空,如果它们实现.length().size()方法,则不为空?

编辑虽然有些答案侧重于实施等效的isEmpty方法(并且pgreen2的答案确实有效), CodaFi 和< em> The Tosters 似乎是利用弱类型的Objective-C编码实践可能无法很好地转换为像Java这样的强类型范例(“方形挂钩,圆孔”)。这并不是说任何一种打字方法都是可取的;他们只是不同,需要不同的做法。

3 个答案:

答案 0 :(得分:3)

在我看来,这是java中非常糟糕的习惯。由于许多实例检查或try / catch递归,此方法将“非常繁重”。此外,如果集合为NULL或它只是空的,上面的一些示例会导致您丢失信息。 在OBJ-C中这没有问题,因为你总是可以在nil指针上调用方法,但这对于java来说是非常不同的。我相信这可能会导致许多问题,正确控制算法。例如,你可能会像

一样
if (Helper.isEmpty(someList) == true) {
    someList = new ArrayList<>();
}

如果这是由多个方法调用作为参数缓冲区传递的数组,这可能会导致很难找到的错误。

基本上输入你的方法就知道出现了什么类型的对象,所以你可以立即进行检查,而不需要进行多种类型的检查方法,如果发现参数类型,你可以从一开始就知道你有哪些知识。

答案 1 :(得分:2)

我没有经验,但看起来它的输入比java弱得多。问题的第一部分非常简单。

//this checks for null objects
public static boolean isEmpty(Object o) {
  return o == null;
}

但是,在示例代码中,您正在检查具有特定名称的特定字段。这在java中要复杂得多。您可以使用reflection,但需要检查方法和字段。然后,您需要检查适当的返回类型:Number,int,long,short,float,double。所以,这是可能的,但需要大量的工作,而且会很慢。

更简单但更具限制性的机制是仅检查常见类型: (更新以修复Enumeration的错误并添加数组;由于数组如何在java中工作,因此必须为每个主要数据分解数组)

public static boolean isEmpty(Object o) {
    if (o == null) {
        return true;
    }
    else if (o instanceof Object[]) {
        return ((Object[]) o).length <= 0;
    }
    else if (o instanceof boolean[]) {
        return ((boolean[]) o).length <= 0;
    }
    else if (o instanceof byte[]) {
        return ((byte[]) o).length <= 0;
    }
    else if (o instanceof short[]) {
        return ((short[]) o).length <= 0;
    }
    else if (o instanceof char[]) {
        return ((char[]) o).length <= 0;
    }
    else if (o instanceof int[]) {
        return ((int[]) o).length <= 0;
    }
    else if (o instanceof long[]) {
        return ((long[]) o).length <= 0;
    }
    else if (o instanceof float[]) {
        return ((float[]) o).length <= 0;
    }
    else if (o instanceof double[]) {
        return ((double[]) o).length <= 0;
    }
    else if (o instanceof CharSequence) {
        return ((CharSequence) o).length() <= 0;
    }
    else if (o instanceof Collection) {
        return ((Collection) o).isEmpty();
    }
    else if (o instanceof Map) {
        return ((Map) o).isEmpty();
    }
    else if (o instanceof Enumeration) {
        return !((Enumeration) o).hasMoreElements();
    }
    else if (o instanceof Dictionary) {
        return ((Dictionary) o).isEmpty();
    }
    else if (o instanceof Iterable) {
        // NOTE: may not be efficient because an iterator is created
        return !((Iterable) o).iterator().hasNext();
    }

  return false;
}

更新:以下是带有反射的先前版本,用于检查方法,代码可以扩展为支持字段。处理返回类型并不像我最初想的那么困难。它甚至可以通过反射来连接自动装箱工作。我还检查了对象是否有一个isEmpty()布尔方法。

public static boolean isEmpty(final Object o) {
    if (o == null) {
        return true;
    }
    else if (o instanceof Object[]) {
        return ((Object[]) o).length <= 0;
    }
    else if (o instanceof boolean[]) {
        return ((boolean[]) o).length <= 0;
    }
    else if (o instanceof byte[]) {
        return ((byte[]) o).length <= 0;
    }
    else if (o instanceof short[]) {
        return ((short[]) o).length <= 0;
    }
    else if (o instanceof char[]) {
        return ((char[]) o).length <= 0;
    }
    else if (o instanceof int[]) {
        return ((int[]) o).length <= 0;
    }
    else if (o instanceof long[]) {
        return ((long[]) o).length <= 0;
    }
    else if (o instanceof float[]) {
        return ((float[]) o).length <= 0;
    }
    else if (o instanceof double[]) {
        return ((double[]) o).length <= 0;
    }
    else if (o instanceof CharSequence) {
        return ((CharSequence) o).length() <= 0;
    }
    else if (o instanceof Collection) {
        return ((Collection) o).isEmpty();
    }
    else if (o instanceof Map) {
        return ((Map) o).isEmpty();
    }
    else if (o instanceof Enumeration) {
        return !((Enumeration) o).hasMoreElements();
    }
    else if (o instanceof Dictionary) {
        return ((Dictionary) o).isEmpty();
    }
    else if (o instanceof Iterable) {
        // NOTE: may not be efficient because an iterator is created
        return !((Iterable) o).iterator().hasNext();
    }

    // reflection code

    final Number length = retrieveNumberFromMethod(o, "length");
    if (length != null) {
        return length.shortValue() <= 0;
    }

    final Number size = retrieveNumberFromMethod(o, "size");
    if (size != null) {
        return size.shortValue() <= 0;
    }

    final Boolean isEmpty = retrieveBooleanFromMethod(o, "isEmpty");
    if (isEmpty != null) {
        return isEmpty;
    }

    return false;
}

static Number retrieveNumberFromMethod(final Object o, final String methodName) {
    try {
        final Number number = (Number) o.getClass().getMethod(methodName).invoke(o);
        return number;
    }
    catch (final IllegalArgumentException e) {
        throw new IllegalStateException("Unable to retrieve number from " + methodName + " on " + o, e);
    }
    catch (final SecurityException e) {
        throw new IllegalStateException("Unable to retrieve number from " + methodName + " on " + o, e);
    }
    catch (final InvocationTargetException e) {
        throw new IllegalStateException("Unable to retrieve number from " + methodName + " on " + o, e);
    }
    catch (final IllegalAccessException e) {
        return null;
    }
    catch (final NoSuchMethodException e) {
        return null;
    }
}

static Boolean retrieveBooleanFromMethod(final Object o, final String methodName) {
    try {
        final Boolean bool = (Boolean) o.getClass().getMethod(methodName).invoke(o);
        return bool;
    }
    catch (final IllegalArgumentException e) {
        throw new IllegalStateException("Unable to retrieve boolean from " + methodName + " on " + o, e);
    }
    catch (final SecurityException e) {
        throw new IllegalStateException("Unable to retrieve boolean from " + methodName + " on " + o, e);
    }
    catch (final InvocationTargetException e) {
        throw new IllegalStateException("Unable to retrieve boolean from " + methodName + " on " + o, e);
    }
    catch (final IllegalAccessException e) {
        return null;
    }
    catch (final NoSuchMethodException e) {
        return null;
    }
}

答案 2 :(得分:1)

在Java中查看课程Class和课程Method,这引导我进行isEmpty的以下实施:

public static boolean isEmpty(Object o) {
   try {
      return o == null ||
            (0 == (int)(((Class<? extends Object>) (o.getClass()))
              .getMethod("size", (Class[]) null).invoke(o, (Object[]) null)));
   } catch (IllegalAccessException | IllegalArgumentException
         | InvocationTargetException | SecurityException e) {
      e.printStackTrace();
      return true;
   } catch (NoSuchMethodException e) {
      return true;
   }
}

对于采用参数的方法,请根据nullgetMethod()的文档替换invoke()Method中的Class

附录

我知道size()返回一个int,所以我将invoke()的结果投射而不受惩罚。 isEmpty()的这一修订实施更清晰,更明确地说明了它的作用:

public static boolean isEmpty(Object o) {
   if (o != null) {
      Class<? extends Object> c = o.getClass();
      try {
         Method m = c.getMethod("size", (Class[]) null);
         Integer result = (Integer) m.invoke(o, (Object[]) null);
         if (result.intValue() > 0) {
            return false;
         }
      } catch (NoSuchMethodException e) {
         // o doesn't have a "size", so we'll quietly move on to return true
         // indicating this object is non-accessible
      } catch (IllegalAccessException | IllegalArgumentException
            | InvocationTargetException | SecurityException e) {
         // got something unexpected, let's show how we got here and then
         // return true, indicating this object is non-accessible
         e.printStackTrace();
      }
   }
   return true;
}