`

返回null还是empty

    博客分类:
  • J2EE
阅读更多

第一个问题,函数是应当返回null还是长度为0的数组(或集合)?
第二个问题,函数输入参数不当时,是异常还是返回null?

先看第一个问题

有两个约定我觉得应当遵守:

1.返回零长度的数组或集合而不是null(详见《Effective Java》)

理由就是,如果返回empty,就可以少了很多not-null判断:
List<Person> list = queryPerson();
if (list != null) {
	for (Person p : list) {
		//do something
	}
}

如果queryPerson永不返回null,那代码可以这样:
	
List<Person> list = queryPerson();
for (Person p : list) {
	//do something
}


遵守这个规则的一个例子就是Spring JdbcTemplate的query方法,在查询不到结果时,返回的是empty List:
RowMapperResultSetExtractor:
	public List<T> extractData(ResultSet rs) throws SQLException {
		List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
		int rowNum = 0;
		
		while (rs.next()) {
			results.add(this.rowMapper.mapRow(rs, rowNum++));
		}
		return results;
	}


2. 不要区分null引用和empty值

引用:

7. 降低修改时的误解性,不埋雷
……
一个原则就是永远不要区分null引用和empty值。

详见http://javatar.iteye.com/blog/1056664

但现实世界没那么理想

比如Spring,在这两个规则上,都没有统一的处理方式

org.springframework.util.StringUtils:
public static String[] tokenizeToStringArray(
		String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {

	if (str == null) {
		return null;
	}
}

public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {

	if (str == null) {
		return new String[0];
	}
}

两个功能相似的函数,在str==null时,一个返回null,一个返回empty
同一个class中都不统一,很奇怪

顺便说一下这两个函数的最大不同:
String str = "a,b;c;,d";
String delimiter = ";,";

//同时把";"和","当作分隔符
String[] sa = StringUtils.tokenizeToStringArray(str, delimiter);
assertEquals(4, sa.length);
assertEquals("a", sa[0]);
assertEquals("b", sa[1]);
assertEquals("c", sa[2]);
assertEquals("d", sa[3]);

//认为";,"是一个分隔符
sa = StringUtils.delimitedListToStringArray(str, delimiter);
assertEquals(2, sa.length);
assertEquals("a,b;c", sa[0]);
assertEquals("d", sa[1]);


Apache StringUtils也有类似的方法,对应关系如下:
Apache													Spring
---------------------------------------------------------
split													tokenizeToStringArray
splitByWholeSeparator							delimitedListToStringArray

两个函数对str==null的处理是统一了,但它认为null和""是不同的情况:
public static String[] split(String str, String separatorChars) {
	return splitWorker(str, separatorChars, -1, false);
}
private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {

	if (str == null) {
		return null;
	}
	int len = str.length();
	if (len == 0) {
		return ArrayUtils.EMPTY_STRING_ARRAY;		//that is, new String[0]
	}
}
	//splitByWholeSeparatorWorker在这方面跟split一样


再看看Apache ArrayUtils:
public static int[] toPrimitive(Integer[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_INT_ARRAY;
        }
}

同样是区分了null和empty

所以,保险起见,对数组和集合还是要加上not-null判断,虽然代码丑了一点;除非代码都是你自己写的或者你看过源码了确保永不返回null

第二个问题

Apache FileUtils和IOUtils,对输入参数为null的处理,很多时候是抛出异常,但有时也会“静静的失败”

Apache FileUtils:
	//抛出异常
    public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
        if (srcDir == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (srcDir.exists() && srcDir.isDirectory() == false) {
            throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
        }
		//...
    }
	
	//注意到这个方法没有考虑file==null的情况
    public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding, boolean append)
            throws IOException {
        FileOutputStream out = null;
        try {
            out = openOutputStream(file, append);
            final BufferedOutputStream buffer = new BufferedOutputStream(out);
            IOUtils.writeLines(lines, lineEnding, buffer, encoding);
            buffer.flush();
            out.close(); // don't swallow close Exception if copy completes normally
        } finally {
            IOUtils.closeQuietly(out);
        }
    }
	
	//what if "file==null" ?
	public static FileOutputStream openOutputStream(File file, boolean append) throws IOException {
        if (file.exists()) {
            if (file.isDirectory()) {
                throw new IOException("File '" + file + "' exists but is a directory");
            }
			//...
        }
        return new FileOutputStream(file, append);
    }


Apache IOUtils:
//不抛异常	
public static void writeLines(Collection<?> lines, String lineEnding, OutputStream output, Charset encoding)
	throws IOException {
	
	if (lines == null) {
		return;
	}
	//...
}


Apache BeanUtils:
//不抛异常
public static void populate(Object bean, Map properties)
        throws IllegalAccessException, InvocationTargetException
    {
        BeanUtilsBean.getInstance().populate(bean, properties);
    }

public void populate(Object bean, Map properties)
    throws IllegalAccessException, InvocationTargetException {

    // Do nothing unless both arguments have been specified
    if ((bean == null) || (properties == null)) {
        return;
    }
	//...
}


看来这个问题也是见仁见智
如果你认为NullPointerException或者IllegalArgumentException有必要通知调用方,那就抛异常,特别是调用方想知道为什么调用失败:
e.g. File存在但没有写权限
e.g. File本应该是文件但传入的是目录

个人总结:
应该采取“防御式编程”,怀疑一切

0
0
分享到:
评论
2 楼 bylijinnan 2014-05-16  
chenchuan 写道
几次线上bug告诉我不相信任何传入的参数

1 楼 chenchuan 2014-05-16  
几次线上bug告诉我不相信任何传入的参数

相关推荐

    一张表搞清楚php is_null、empty、isset的区别

    如果变量不存在或者其值等价于逻辑假(如0、空字符串、NULL、FALSE等),`empty()` 返回 `true`;否则返回 `false`。这里的“空”不仅仅是变量未定义,也包括变量值为0或FALSE等情况。表格中的对应结果如下: ``` $...

    ASP中Null,Empty,Nothing的区别分析

    在ASP(Active Server Pages)开发中,理解和区分`Null`、`Empty`和`Nothing`是非常重要的,因为它们各自代表不同的空值状态,且在处理时有着特定的规则。接下来,我们将深入探讨这三个概念的区别以及如何进行正确的...

    PHP中empty,isset,is_null用法和区别

    在处理数组元素时,比如表单提交的场景,`empty()` 适合用来检查变量是否有非空值,例如 `$_REQUEST['status'] = 0`,`empty($_REQUEST['status'])` 返回 `TRUE`。而 `isset()` 则用于确认变量是否已定义,即使变量...

    el表达式empty的用法

    2. **当变量值为`null`时**:如果变量已经被定义,但其值为`null`,`empty`操作符同样会返回`true`。 3. **当变量为字符串且长度为0时**:如果变量是一个字符串类型,并且该字符串的长度为0(即空字符串),那么`...

    php中is_null,empty,isset,unset 的区别详细介绍

    一旦使用 `unset()`,变量将不再存在,之后的 `isset()` 和 `empty()` 都会返回 `false`,而 `is_null()` 无法使用,因为它不再是一个有效的变量。 ```php $a = "Hello"; unset($a); if (isset($a)) { echo ...

    php empty,isset,is_null判断比较(差异与异同)

    - `empty` 和 `isset` 都会在变量未定义时抛出错误,而 `is_null` 可以接受未定义的变量,但会返回 `false`。 - `empty` 和 `isset` 接受的参数必须是变量,而 `is_null` 可以接受变量、常量、表达式等。 **使用...

    PHP 判断数组是否为NULL的5大方法

    当数组未被定义或者其值等价于FALSE(包括NULL)时,`empty()`返回TRUE。所以,这个函数也可以用来检查数组是否为NULL: ```php $array = NULL; if (empty($array)) { echo "数组是NULL"; } else { echo "数组...

    PHP中空字符串介绍0、null、empty和false之间的关系

    因此,空字符串、0、null以及false都会使empty()返回true,除了0之外,因为0在布尔上下文中有意义,被认为是true。 false是布尔值的一种,它在逻辑上与true相反。在比较中,false不等于0、空字符串或null。然而,当...

    php中0,null,empty,空,false,字符串关系的详细介绍

    由于null在布尔上下文中也是被视为假(false),所以使用empty()来检查null变量也会返回true。 6. false和empty: 由于false是一个布尔值,在使用empty()函数进行检查时同样会返回true。 7. 空字符串和empty: 空...

    PHP中的empty、isset、isnull的区别与使用实例

    如果 变量 是非空或非零的值,则 empty() 返回 FALSE。换句话说,””、0、”0″、NULL、FALSE、array()、var $var、未定义;以及没有任何属性的对象都将被认为是空的,如果 var 为空,则返回 TRUE。 代码示例: $a ...

    PHP判断和比较null、0、''、false的正确姿势.docx

    如果变量不存在、值为null、0、''、false或非数组类型的空数组,empty()都会返回true。 - `is_null()`:这个函数专门用来判断变量是否为null,其他任何值都不会使其返回true。 - `isset()`:该函数用于检查变量...

    解析PHP中empty is_null和isset的测试

    - empty($e) 返回true,因为$e是null。 - empty($f) 返回true,因为$f是一个空数组。 其次,is_null()是一个函数,它用于检查变量是否为NULL。与其他两种检测方法不同,is_null()只检查变量是否被明确地赋值为NULL...

    ADODB rs.fields()不能为空的解决方案

    这通常涉及到自定义一个函数来判断变量是否为 Null 或者 Empty,然后根据结果决定是否返回默认值或进行其他处理。 示例代码展示了如何使用 VBA(Visual Basic for Applications)编写这样的函数: ```vb Public ...

    lua读取redis数据的null判断示例代码

    由于Redis可能返回`ngx.null`,我们需要同时检查`ngx.null`和`nil`。如果`access_token`等于`ngx.null`或`nil`,说明没有有效的token,可以执行清理或错误处理的逻辑。如果`access_token`非空,通常我们会进一步处理...

    还在用if(obj!=null)做非空判断,带你快速上手Optional

    Optional 是 Java 8 中引入的新特性,旨在解决空指针异常(Null Pointer Exception,NPE)这个长期困扰开发者的问题。下面我们将对 Optional 的基本概念、使用方法、优点等进行详细的介绍。 Optional 的基本概念 ...

Global site tag (gtag.js) - Google Analytics