我知道孩子不能降低非静态方法的可见性,并且我理解为什么会这样。
但是,我读过“静态方法可以通过重新声明来隐藏”。但是,我不明白如何在 Java 中实现这一点。
这真的可能吗?如果是,怎么做(代码示例)以及为什么引入它(这似乎与不降低界面可见性的原则相矛盾)?
我知道孩子不能降低非静态方法的可见性,并且我理解为什么会这样。
但是,我读过“静态方法可以通过重新声明来隐藏”。但是,我不明白如何在 Java 中实现这一点。
这真的可能吗?如果是,怎么做(代码示例)以及为什么引入它(这似乎与不降低界面可见性的原则相矛盾)?
简短的回答是:不,这是不可能的。你混淆了一些术语。隐藏与可访问性无关(这是您真正要问的问题,而不是可见性,它与范围和阴影有关,在 Java 语言规范 (JLS) 的第 6 章中进行了讨论)。
现在是更长的答案。术语覆盖适用于实例方法,而术语隐藏适用于类(static
)方法。来自Java 教程主题覆盖和隐藏方法:
隐藏静态方法和覆盖实例方法之间的区别具有重要意义:
- 被调用的重写实例方法的版本是子类中的版本。
- 被调用的隐藏静态方法的版本取决于它是从超类调用还是从子类调用。
这里的其他一些答案提供了关于方法隐藏的错误示例,所以让我们回到 JLS,这次是§8.4.8:
方法在逐个签名的基础上被覆盖或隐藏。
也就是说,要覆盖或隐藏父类中的方法,子类必须定义一个具有相同签名的方法——基本上,相同数量和类型的参数(尽管泛型和类型擦除使规则比这更复杂一些) . 还有关于返回类型和throws
子句的规则,但这些似乎与这个问题无关。
请注意,您可以在子类中定义与父类(或实现的接口)中的方法同名但参数数量或类型不同的方法。在这种情况下,您正在重载方法名称,既没有覆盖也没有隐藏任何东西;子类方法是一种新方法,几乎独立于继承的方法。(当编译器必须将方法与方法调用相匹配时,存在交互,仅此而已。)
现在回答您的问题:可访问性和隐藏(以及可见性)这两个术语是 Java 中的独立概念。正如您所说,有一个“原则”,即子类根本无法减少继承方法的可访问性。无论您是覆盖实例方法还是隐藏类方法,这都适用。从JLS §8.4.8.3:
覆盖或隐藏方法的访问修饰符 ( §6.6 ) 必须至少提供与覆盖或隐藏方法一样多的访问权限,如下所示:
如果覆盖或隐藏的方法是
public
,那么覆盖或隐藏的方法必须是public
;否则,会发生编译时错误。如果覆盖或隐藏的方法是
protected
,那么覆盖或隐藏的方法必须是protected
orpublic
;否则,会发生编译时错误。如果覆盖或隐藏方法具有默认(包)访问权限,则覆盖或隐藏方法不得为
private
; 否则,会发生编译时错误。
总而言之,static
可以隐藏方法的事实与更改方法的可访问性无关。
根据 hagubear 的宝贵意见,似乎声明的作者意味着通过用具有相同声明的方法重载方法来隐藏方法。
引用此链接:
我们可以在子类中声明具有相同签名的静态方法,但它不被视为重写,因为不会有任何运行时多态性。(...) 如果派生类定义了与基类中的静态方法具有相同签名的静态方法,则派生类中的方法会隐藏基类中的方法。
因此,在具有完全相同声明的子类中定义方法有效地隐藏了子类中的原始方法。但是,与字段一样,强制转换为父级将恢复原始访问权限。
示例代码:
public class Test {
public static void main( String[] args ) {
B b = new B();
A a = b;
b.f(); // "Access somewhat denied"
a.f(); // "f()"
}
}
class A {
public static void f() {
System.out.println("f()");
}
}
class B extends A {
// *must* be public
public static void f() {
System.out.println("Access somewhat denied");
}
}
所以我创建了一个简单的测试;IntelliJ 确实拒绝了它……是的,我知道“它是一种工具……但我信任它”。无论如何,我去了 javac,它发出了同样的错误:
Error:(...) java: ...Concrete.java:5: doSomethingStatic() in
...Concrete cannot override doSomethingStatic() in
...Base; attempting to assign weaker access privileges; was public
基于此,以及我们普遍的怀疑态度,我建议您的文档中存在错误。
下面是我的示例代码,我认为相当明确。它在protected
.
public class Base
{
public static void doSomethingStatic(){}
}
public class Concrete extends Base
{
protected static void doSomethingStatic(){}
}
它可以通过派生类中的重载重新声明来隐藏:
class Base
{
public static void doSomethingStatic(){}
}
class Derived extends Base
{
public static void doSomethingStatic(String arg){}
}
但仅对尝试通过派生类访问它的人隐藏。