我有一个静态导入org.junit.Assert.assertEquals
方法的Junit4
测试用例。
import static org.junit.Assert.assertEquals;
在这个类中,我创建了一个实用程序方法,用于断言一些复杂的内部类,这些类不实现相等(并且实现它也很难)。
private void assertEquals(MyObj o1, MyObj o2)
{
assertEquals(o1.getSomething(), o2.getSomething());
assertEquals(o1.getSomethingElse(), o2.getSomethingElse());
...
}
我期望代码表现得好像我正在"重载"我正在导入的assertEquals
方法,但看起来我的私有非静态方法是隐藏静态导入的方法。我也试着把我的方法变成public
和static
(所有的排列),但没有成功-我不得不重命名它。
为什么会这样?我在文档中找不到任何关于此行为的参考
你观察到的是所谓的阴影。当java中的两个类型具有相同的简单名称时,其中一个将遮蔽另一个。因此,shadow类型不能通过它的简单名称来使用。
最常见的遮蔽类型是隐藏字段的参数。通常会导致setter代码看起来像setMyInt(int myInt) {this.myInt = myInt; }
现在让我们阅读相关文档:
static-import-on-demand声明永远不会导致任何其他声明被遮蔽。
这表明静态按需导入总是出现在最后,所以任何与按需导入声明具有相同简单名称的类型将总是遮蔽(隐藏)静态导入。
重载和覆盖在继承树中工作。但是静态导入并不构建继承。
如果你想在你自己的assertEquals方法中使用junit的assertEquals,你必须用className来限定它,例如Assert.assertEquals.
使用非静态导入org.junit.Assert
您无意中发现了方法隐藏,即局部方法的存在将一个方法从另一个类(通常是超类)中"隐藏"起来。
我一直觉得静态导入方法虽然在语法上是可能的,但在某种程度上是"错误的"。
作为一种风格,我更喜欢导入类并在我的代码中使用TheirClass.method()
。这样做可以清楚地表明该方法不是一个局部方法,而好的代码的标志之一就是清晰。
我推荐你使用import org.junit.Assert
和Assert.assertEquals(...)
。
这很有意义。假设javac做了您想要的,它今天选择您的assertEquals(MyObj, MyObj)
方法。如果明天org.junit.Assert
添加了自己的assertEquals(MyObj, MyObj)
方法怎么办?调用assertEquals(mo1,mo2)
的含义在您不知情的情况下发生了戏剧性的变化。
问题是assertEquals
这个名字的含义。Javac需要确定这是org.junit.Assert
中方法的名称。只有这样,它才能做方法重载解析:检查org.junit.Assert
中所有名为assertEquals
的方法,选择最合适的一个。
可以想象java可以处理来自多个类的方法重载,但是正如第一段所示,这给开发人员带来了很大的不确定性,他正在调用哪个类的方法。因为这些类是不相关的,所以方法语义可能差别很大。
如果在编译时,对于开发人员来说方法属于哪个类是毫无疑问的,那么该类仍然有可能在明天重载该方法,从而改变所调用的目标方法。然而,因为这是由同一个类完成的,我们可以让它负责。例如,如果org.junit.Assert
决定添加一个新的assertEquals(MyObj, MyObj)
方法,它必须意识到以前对assertEquals(Object,Object)
的一些调用现在被重路由到新方法,并且必须确保没有语义更改会破坏调用站点。