`

返回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中empty,isset,is_null用法和区别

    如果 var 是非空或非零的值,则 empty() 返回 FALSE。换句话说,””、0、”0″、NULL、FALSE、array()、var $var; 以及没有任何属性的对象都将被认为是空的,如果 var 为空,则返回 TRUE 2.isset() isset — 检测...

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

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

    java中optional妙用解决NullPointerException

    创建一个空的 Optional 实例(这里返回一个为Null的optional) Optional&lt;String&gt; empty = Optional.empty(); Optional.of(T t); 创建一个 Optional 实例,当 t为null时抛出异常(of 方法的值不能为空否则会抛出异常) ...

    PHP中isset、empty的用法与区别示例详解

    前言 在编写程序调用变量时,遇到未定义的变量...若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。 同时要注意的是 null 字符(”\0″)并不等同于 PHP 的 NULL 常量。 注:如果一次传入多个参数,那么 i

    PHP isset()及empty()用法区别详解

    若变量存在且其值为NULL,也返回FALSE 若变量存在且值不为NULL,则返回TURE 同时检查多个变量时,每个单项都符号上一条要求时才返回TRUE,否则结果为FALSE 如果已经使用unset()释放了一个变量之后,它将不再是isset()...

    php empty() 检查一个变量是否为空

    empty — 检查一个变量是否为空 Report a bug 描述 bool empty ( mixed $var ) 如果 var 是非空或非零的值,则 empty() 返回 FALSE。换句话说,””、0、”0″、NULL、FALSE、array()、var $var; 以及没有任何属性的...

    php中isset与empty函数的困惑与用法分析

    isset():用于判断一个函数是否被设置过,如果设置过就为true,否则就为false,但是有一个例外,就是如果一个变量被设置成null的话,此时也会返回的也是false。 &lt;?php $a = null; var_dump(isset($a));//a变量...

    laravel 判断查询数据库返回值的例子

    在使用 Laravel Eloquent 模型时,我们可能要判断取出的结果集是否为空,但我们发现直接使用 is_null 或 empty 是无法判段它结果集是否为空的。 var_dump 之后我们很容易发现,即使取到的空结果集, Eloquent 仍然会...

    VBSCRIPT中文手册

    IsNull 函数 返回 Boolean 值,表示表达式是否含有无效数据 (Null)。 IsNumeric 函数 返回 Boolean 值,表示表达式能否当作一个数,用来计算。 IsObject 函数 返回 Boolean 值,表示表达式是否引用了有效的“自动...

    vb Script参考文档

    IsNull 函数 返回 Boolean 值,表示表达式是否含有无效数据 (Null)。 IsNumeric 函数 返回 Boolean 值,表示表达式能否当作一个数,用来计算。 IsObject 函数 返回 Boolean 值,表示表达式是否引用了有效的“自动...

    VBScript 语言参考

    IsNull 函数 返回 Boolean 值,表示表达式是否含有无效数据 (Null)。 IsNumeric 函数 返回 Boolean 值,表示表达式能否当作一个数,用来计算。 IsObject 函数 返回 Boolean 值,表示表达式是否引用了有效的“自动...

    VBScript 语言参考中文手册CHM

    IsNull 函数 返回 Boolean 值,表示表达式是否含有无效数据 (Null)。 IsNumeric 函数 返回 Boolean 值,表示表达式能否当作一个数,用来计算。 IsObject 函数 返回 Boolean 值,表示表达式是否引用了有效的“自动...

    VBSCRIP5 -ASP用法详解

    IsNull 函数 返回 Boolean 值,表示表达式是否含有无效数据 (Null)。 IsNumeric 函数 返回 Boolean 值,表示表达式能否当作一个数,用来计算。 IsObject 函数 返回 Boolean 值,表示表达式是否引用了有效的“自动...

    c#中Empty()和DefalutIfEmpty()用法分析

    本文实例分析了c#中Empty()和DefalutIfEmpty()用法。分享给大家供大家参考。具体分析如下: 在项目中,当我们想获取IEnumerable集合的时候,这个集合有可能是null。但通常的做法是返回一个空的集合。 假设有这样一个...

    mysqlhelper

    command.Parameters.Add(new SqlParameter("ReturnValue", SqlDbType.Int, 4, ParameterDirection.ReturnValue, false, 0, 0, string.Empty, DataRowVersion.Default, null)); return command; } /// /// 执行...

    PHP表单验证的3个函数ISSET()、empty()、is_numeric()的使用方法

    定义和作用范围:用于测试一个变量是否具有值(包括0,FALSE,或者一个空字串,但不能是NULL),即:“http://localhost/?fo=”也是可以通过检测,因此不适用。但如果是“http://localhost/”参数中并不含fo参数,...

    海康卫视开发dll文件

    #region using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using SocketLibrary;... /// &lt;summary...

    PHP 的比较运算与逻辑运算详解

    1、以下值用 empty() 被判断为true: ...2、被 isset() 判断为 false 的值:未赋值变量、未声明变量、null、__get() 返回的值,接受的参与 empty()一样,不能是常量和表达式. 3、不同类型的数据比较 如果有一个是 b

    php7新特性的理解和比较总结

    语法: 如果变量存在且值不为NULL,它就会返回自身的值,否则返回它的第二个操作数. //php7以前 if判断 if(empty($_GET['param'])) { $param = 1; }else{ $param = $_GET['param']; } //php7以前 三元运算符 ...

    PHP开发中常用的三个表单验证函数使用小结

    定义和作用范围:用于测试一个变量是否具有值(包括0,FALSE,或者一个空字串,但不能是NULL),即:“http://localhost/?fo=”也是可以通过检测,因此不适用。但如果是“http://localhost/”参数中并不含fo参数,...

Global site tag (gtag.js) - Google Analytics