当类具有与导入的方法同名的方法时,导入静态不起作用



我有一个静态导入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方法,但看起来我的私有非静态方法是隐藏静态导入的方法。我也试着把我的方法变成publicstatic(所有的排列),但没有成功-我不得不重命名它。

为什么会这样?我在文档中找不到任何关于此行为的参考

你观察到的是所谓的阴影。当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.AssertAssert.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)的一些调用现在被重路由到新方法,并且必须确保没有语义更改会破坏调用站点。

相关内容

  • 没有找到相关文章

最新更新