我是一名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这样的强类型范例(“方形挂钩,圆孔”)。这并不是说任何一种打字方法都是可取的;他们只是不同,需要不同的做法。
答案 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;
}
}
对于采用参数的方法,请根据null
和getMethod()
的文档替换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;
}