assert, commons lang validate和Google Guava Preconditions.*

来源:互联网 发布:淘宝拍卖车怎么看到 编辑:程序博客网 时间:2024/06/05 12:43

在写代码时,经常困扰于要检查参数,这还算好,反而是函数体中需要经常进行是否为null的检查,搞得代码很不友好。

比如说调用某个service,首先要handle exception,拿到返回值还要进行null值检查,经常出现类似下面的代码:

Account account;try {    account = accountService.getAccountByID(accountID);} catch (AccountServiceException e) {    throw new ApplicationException("Meet error when querying account by id:" + accountID);}if (account == null)   throw new ApplicationException("Can't get account by id:" + accountID);
我不知道有没有更好的办法,但是我是经常需要这样去判断。最近正好花了点时间集中研究一下如何让这段代码更简洁。

提到参数检查或者null值检查,我们可以用apache commons lang里面的validate,也可以用Google Guava里面的Preconditions类,有一位帅哥Piotr Jagielski总结得非常好,我就直接摘录过来了:

http://piotrjagielski.com/blog/google-guava-vs-apache-commons-for-argument-validation/

Google Guava vs Apache Commons for Argument Validation

It is an established good practice to validate method arguments at the beginning of the method body. For example you could check that the passed value is not negative before doing some calculation:

1
2
3
4
5
6
publicintdoSomeCalculation(intvalue) {
    if(value < 0) {
        thrownewIllegalArgumentException("negative value");
    }
    ...
}

It can be written slightly shorter using the good old Apache Commons:

Validate.isTrue(value >= 0,"negative value");

More recently the same behavior can be achieved with Google Guava after statically importing the Preconditions class:

checkArgument(value >= 0,"negative value");

At surface it looks quite similar but usually the devil is in the details so I decided to take a closer look at both approaches. In this post I’m going to describe the differences between the two libraries regarding argument validation.

Static imports

The first immediately obvious difference is that Guava requires static import to look nice.Validate.isTrue() looks better than Preconditions.checkArgument(). Personally, I don’t mind static imports in my code. For some people it might be a small disadvantage though. Especially for those who don’t know how to work with them in the IDE yet. Also, if you work on some ancient project that still uses Java earlier than 5.0 you won’t be able to use Guava at all.

Types of exceptions

All methods from Validate class in Apache Commons throw IllegalArgumentException when validation fails. Sometimes it make sense to throw a different exception type. Guava make it possible. For example:

  • checkArgument throws IllegalArgumentException
  • checkState throws IllegalStateException
  • checkNotNull throws NullPointerException
  • checkElementIndex and checkPositionIndex throw IndexOutOfBoundsException, etc.

It’s obvious from the method name what exception gets thrown. I like this clear distinction in Guava.

Message parameters

It’s a good idea to give as much information about the failure as possible. For example if you validate that a number is positive you should add the actual number in the exception message if it’s not. In the example below it is done using the string concatenation directly:

Validate.isTrue(i > 0,"Should be positive but was: " + i);

In Commons Validate.isTrue() method you can pass additional parameter instead. It offers a performance benefit because the string concatenation is actually done only when the validation fails.

Validate.isTrue(i > 0,"Should be positive but was: ", i);

You can do a similar thing in Guava:

checkArgument(i > 0,"Should be positive but was: %s", i);

Additionally, Guava uses varargs for message parameters and you could also write:

checkArgument(i > MIN, "Expected more than %s, got %s", MIN, i);

In this case it’s also more readable than using string concatenation:

checkArgument(i > MIN, "Expected more than " + MIN + ", got " + i);

Validating collections and arrays

Apache Commons has some additional validations for collection and arrays that you won’t find in Guava:

  • allElementsOfType(Collection collection, Class clazz)
  • Validate.notEmpty(Collection collection)
  • Validate.notEmpty(Map map)
  • Validate.notEmpty(Object[] array)
  • Validate.noNullElements(Collection collection)
  • Validate.noNullElements(Object[] array)

This first one might be handy in legacy project not using generics. Others are generally useful.

On the other hand you can combine Guava Preconditions with any utility methods. In the example below I use the isNotEmpty method from Commons CollectionUtils in conjunction with Guava Preconditions to ensure that the list is not null and not empty:

checkArgument(isNotEmpty(list));

Assignment after validation

It’s common to assign a method argument to a field after validation. For example withValidate from Apache Commons you could write:

1
2
3
4
publicClass(Object parameter) {
    Validate.notNull(parameter);
    this.field = parameter;
}

The checkNotNull from Guava Preconditions returns the validated reference. This allows validation and assignment in one line:

1
2
3
publicClass(Object parameter) {
    this.field = checkNotNull(parameter);
}

Summary

In summary, here are the main advantages of both classes:

Apache Commons Validate

  • Works in old versions of Java
  • Readable without static imports
  • Collection and array validations

Google Guava Preconditions

  • Additional exception types
  • Better handling of message arguments
  • Easy assignment after not null check
于是上面提到的代码如果用Guava来做检查的话,就可以简写为:

Account account;try {    account = checkNotNull(accountService.getAccountByID(accountID));} catch (Exception e) {    throw new ApplicationException("Meet error when querying account by id:" + accountID);}

当然如果你需要更友好的提示,你可以用两个catch子句。


此外,说到类似的检查。J2SE1.4以后还增加了一个关键字assert专门来做检查。但是它的用法和上面两者有一定的区别,根据assert的设计,它更多地是用来查错的,比如说你在实现某个接口,并且假定其中某几个参数必须符合某种条件,但是保不齐某个调用者没有遵从这个约定,那你就可以加上几个assert来做出及时的提醒,主要用于开发阶段,方便快速地发现问题。比如上面那个例子就不适合用assert。


stackflow上的一篇讨论:When to use assert() and when to use try catch?挺好的:

http://stackoverflow.com/questions/4333371/when-to-use-assert-and-when-to-use-try-catch

用法可以参考Using Assertions in Java Technology这篇文章

http://java.sun.com/developer/technicalArticles/JavaLP/assertions/