使用FindBugs检查你的返回值

有一天我在写代码时犯了一个愚蠢的错误,花了差不多一个小时才搞定。我们都可能遇到这种情况,最后归咎于睡眠不好、头疼或者其他因素比如邻桌的同时在整天写哼着Gotye的“我认识的某人”(也许你听说过这首歌)让你无法集中精力写代码诸如此类。

不管实际情况如何,下面就是我当时遇到的错误:

// 如果finishedTest是一场考试或者
// practiceTest,转到下一章节或段落
getNextChapterParagraph(chapters, taskContentIdentifier);
if (taskContentIdentifier == null) return null;
return new Task(taskContentIdentifier, Type.ASSESSMENT);

getNextChapterParagraph()函数实现如下:

/**
 * 根据当前章节或短乱列表返回下一段或章节内容
 */
private ContentIdentifier getNextChapterParagraph(
    List<Chapter> chapters,
    ContentIdentifier contentIdentifier) {

  // 这里进行一些处理.....

  return new ContentIdentifier(
    chapter.getStream().getId(),
    chapter.getContentId(),
    paragraph.getContentId());
},

这里为了简化我略过了一些代码,但是我相信一些人已经发现了错误。getNextChapterParagraph()方法的对传入的第二个参数ContentIdentifier没有进行修改,而是直接返回了一个新实例。然而我写的代码确信已经更新了taskContentIdentifier的状态。天哪!

一个更插件的情况是在不可改变的对象上调用方法,像是String。下面是一个示例:

String string = "this is a test";
string.substring(10);	// 糟糕!

当然,你可以在单元测试中捕捉到这些错误,这也是我第一次发现此类错误的地方。但是为什么不能在犯错的同时就立刻发现呢?

错在何处?

上面的代码究竟错在何处? 简而言之:getNextChapterParagraph (和String.substring)方法的唯一目的就是返回值,它并没有改变作为参数传入的对象状态。换句话说,它没有发生其他作用。因此,如果调用该方法我们应该会要对返回值做些改变。否则我们为什么要调用它?(当然有一种情况例外,我会在后面讨论到)

在我们调用了一个没有产生作用的函数时,我们希望编译器或者其他工具能够给出警告并丢弃返回值。解决的办法应该包含两部分:

  1. 标记出没有起作用的函数 (也许静态代码分析可以确定方法是否没有起作用,但是那不是本文讨论的内容)
  2. 在没有起作用的函数调用时给出警告并丢弃返回值(也就是说,没有赋值给变量,没有用于表达式和没有传递给其他函数)

解决错误:FindBugs和@CheckReturnValue

或许你可能熟悉FindBugs,这款代码分析工具恰好能够满足这样的要求。接下来我会继续介绍在Eclipse中如何设置FindBugs。首先,针对我们的问题:可以使用 @CheckReturnValue注解。简单的说@CheckReturnValue会标记没有起作用的函数。当FindBugs检查到一个函数调用返回值被丢弃时会给出警告。

要使用 @CheckReturnValue 你需要在classpath上添加findbugs-annotations.jar。Maven用户可以在POM依赖关系里添加下面的依赖:

<dependency>
<groupId>findbugs</groupId>
<artifactId>annotations</artifactId>
<version>1.0.0</version>
</dependency>

调用@CheckReturnValue的示例如下:

package nl.trifork.examples.checkreturnvalue;

import edu.umd.cs.findbugs.annotations.CheckReturnValue;

public class StringHelper {

	@CheckReturnValue
	public String shift(String str) {
		if (str.length() < 2) return str;
		return str.substring(1) + str.charAt(0);
	}
}

运行FindBugs会产生下面的警告:

Bug: return value of StringHelper.shift(String) ignored in nl.trifork.examples.checkreturnvalue.CheckReturnValueExample.testStringHelper()

现在,我们还不能给JDK中的类添加@CheckReturnValue,但是幸运的是FindBugs已经默认对这些函数做了检查:

public class CheckReturnValueExample {

	public void jdkStringExample() {
		String str = "this is a test";
		str.substring(10);
		System.out.println(str);
	}
}

FindBugs:

Bug: return value of String.substring(int) ignored in nl.trifork.examples.checkreturnvalue.CheckReturnValueExample.jdkStringExample()

例外

那么有没有一些情况下确实需要调用不产生作用的方法呢?实际上确实有,但是似乎并没有那些函数不会真正产生作用。考察一下String.substring():

substring

public String substring(int beginIndex)

返回当前String的字串,字串包含从指定的index开始直到String结尾的所有字符。

示例:

 "unhappy".substring(2) returns "happy"
 "Harbison".substring(3) returns "bison"
 "emptiness".substring(9) returns "" (an empty string)

参数:

beginIndex – 起始index,包含该位置的字符

返回值:

指定的字串

异常:

IndexOutOfBoundsException – 如果 beginIndex 为负数或者大于String对象的长度会抛出异常

比如我们想要编写一个substring的单元测试并传入一个负数作为beginIndex,这时应该抛出一个IndexOutOfBoundsException异常:

@Test(expected=IndexOutOfBoundsException.class)
public void testSubstring() {
	String str = "this is a test";
	str.substring(-1);
}

如果我们运行FindBugs会得到下面的结果:

Bug: return value of String.substring(int) ignored in nl.trifork.examples.checkreturnvalue.CheckReturnValueExample.testSubstring()

上面的情况简单地说,既然我们没有在正常的情况(非测试代码)下调用函数,上面的测试类就不应该被FindBugs检查(也就是为什么他们被叫做例外的原因)。或者说通过设置开启的检查来实现例外。

IDE集成

现在每次在检查代码质量的时候运行FindBugs是很好的办法,但是对“检查返回值”我们需要能够得到即时的提醒。否则等待你运行FindBugs的时候,可能需要花费数小时来定位错误。我们希望FindBugs能够在每次编译器增量编译时执行检查。Eclipse的FindBugs插件可以满足要求。

安装FindBugs的最简单的办法就是下载SpringSource工具集,SpringSource版本的Eclipse IDE。在仪表板(Dashboard)视图,你可以选择FindBugs,点击安装、同意协议、点击几次下一步然后点击完成,等一会Eclipse重启后FindBugs就已经安装就绪可以使用了。

Install-FindBugs

在SpringSource工具集上安装FindBugs插件

“普通”Eclipse用户可以通过http://findbugs.cs.umd.edu/eclipse地址下载安装FindBugs。IntelliJ用户:相信你已经安装了FindBugs。

默认情况下FindBugs只在项目或类的右键菜单里点击Find Bugs时才会运行。然而,你可以让FindBugs在每次构建代码(通常是执行保存)时自动运行。选中你的项目,打开项目属性(*nix和Windows上输入Alt-Enter 或者 在Mac上输入⌘I)。在面板的左侧找到FindBugs,接着选中“自动运行”复选框。

FindBugs-Run-automatically1

设置FindBugs插件为“自动执行”

现在,每当你保存类文件时FindBugs都会运行并且你能立刻看到FindBugs的警告信息。警告信息会出现在代码左侧的“灰色”条上:

FindBugs-warning

一个FindBugs警告

点击Bug标记就可以看到bug的详细信息。

The FindBugs Bug Info panel in Eclipse

Eclipse中的FindBugs信息

结论

采用@CheckReturnValue 注解和FindBugs可以在你忘记使用返回值的时候给出警告。所以行动吧,为所有没有起作用的函数加上 @CheckReturnValue(还有那些getter方法!)并让FindBugs作为你构建的一部分。我一定会这么干。

向更好的代码前进!

 

原文链接: blog.trifork.com 翻译: ImportNew.com - 唐尤华
译文链接: http://www.importnew.com/5092.html
[ 转载请保留原文出处、译者和译文链接。]

关于作者: 唐尤华

我喜欢程序员,他们单纯、固执、容易体会到成就感;面对压力,能够挑灯夜战不眠不休;面对困难,能够迎难而上挑战自我。他们也会感到困惑与傍徨,但每个程序员的心中都有一个比尔盖茨或是乔布斯的梦想“用智慧开创属于自己的事业”。我想说的是,其实我是一个程序员。(新浪微博:@唐尤华

查看唐尤华的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部