考虑以下代码:
@Test
public void testDeadCode() {
letsThrow();
System.out.println("will never be reached");
}
private final void letsThrow() {
throw new RuntimeException("guess you didnt see this one coming");
}
对我来说,执行println()似乎是绝对不可能的,因为对letsThrow()的调用总是会引发异常。
因此我是
a) 很惊讶编译器不能告诉我"这是死代码">
b) 想知道是否有一些编译器标志(或eclipse设置)会告诉我:你在那里得到了死代码。
死代码编译时错误是由编译器而不是IDE定义的。虽然代码永远不会被执行,但它并没有违反Oracle文档中不可访问语句的任何规则。
来自无法访问的语句
本节专门对单词";可达"其思想是,从包含语句的构造函数、方法、实例初始化器或静态初始化器的开头到语句本身,必须有一些可能的执行路径。分析考虑了报表的结构。除了while、do和条件表达式具有常数值true的语句的特殊处理外,在流分析中不考虑表达式的值。
专门针对这种情况的规则与您创建的块是否可访问有关。(iff=当且仅当)
非开关块的空块可以正常完成,前提是它是可访问的。
非开关块的非空块可以正常完成,如果其中的最后一条语句可以正常完成。
非空块中的第一个语句不是交换机块,当该块是可访问的时,该语句是可访问。
在不是切换块的非空块中的每一个其他语句S都是可到达的,前提是S之前的语句可以正常完成。
letsThrow
方法满足代码工作块的标准,并且从技术上来说正常完成。它抛出一个异常,但它完成了。在确定该代码块是否在其实际使用中时,不考虑它是否抛出有保证的异常,只考虑是否可以到达。在大多数情况下,只有当涉及try/catch/returns时,才会发现死代码,而try/catch/returns是规则的主体。
考虑以下更简洁的版本:
@Test
public void testDeadCode() {
System.exit(0);
System.out.println("will never be reached");
}
除了勤奋地使用覆盖工具之外,没有真正的对策,但在您的示例中,好的一面是每次运行代码时都会看到有保证的异常。
旨在进行全面的单元测试,并测量测试的测试覆盖率。死代码将是显而易见的,因为您的任何测试都不会导致它被执行。
将方法声明为返回可丢弃类型:
private final RuntimeException letsThrow() {
throw new RuntimeException("guess you didnt see this one coming");
}
然后你可以在你叫它的时候扔:
throw letsThrow();
现在,调用letsThrow()
之后的任何代码都将被检测为dead。
您可以通过使用静态分析工具检查未使用letsThrow()
返回值的情况来强制执行此操作。例如,Google的易出错程序有一个@CheckReturnValue
注释的检查器,可以确保您使用结果。
(对于穷人的版本,请搜索正则表达式^s*letsThrow();
)。