【问题标题】:Reducing the visibility of a static method降低静态方法的可见性
【发布时间】:2015-01-13 20:12:11
【问题描述】:

我知道孩子不能降低非静态方法的可见性,并且我理解为什么会这样。

然而,我读过“静态方法可以通过重新声明来隐藏”。但是我不明白如何在 Java 中实现这一点。

这真的可能吗?如果是,怎么做(代码示例)以及为什么要引入(似乎与不降低界面可见性的原则相矛盾)?

【问题讨论】:

  • 你有第二个声明的参考/链接吗?
  • 除非我弄错了,否则很可能意味着覆盖静态方法。当你重写时,你只改变了方法的主体——其他一切,即返回类型、方法名称和签名保持不变。这是排序或重新声明。重载也应该起作用。
  • 确实,Eclipse 不允许我覆盖它,它的处理方式与非静态方法相同。所以不是 Eclipse,是 Java 的限制,对吧?
  • @infoholic_anonymous 然后重载 - geeksforgeeks.org/… 另外,不要再说“Eclipse 不允许我覆盖/重载...” Eclipse 是一个工具。即使您使用 IntelliJ、Notepad++ 等,您也会遇到同样的问题。这是一个与 Java 语言相关的问题。工具就是工具。

标签: java inheritance information-hiding


【解决方案1】:

所以我创建了一个简单的测试; 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(){}
}

【讨论】:

    【解决方案2】:

    它可以被派生类中的重载重新声明隐藏:

    class Base
    {
        public static void doSomethingStatic(){}
    }
    
    class Derived extends Base
    {
        public static void doSomethingStatic(String arg){}
    }
    

    但仅对尝试通过派生类访问它的人隐藏。

    【讨论】:

    • 这不是隐藏任何东西的例子。 Derived 中的 doSomethingStatic 方法与 Base 中的方法没有相同的签名——它们有不同的参数列表。
    • 这是 JLS 定义的意义上的“隐藏”,伙计们。没有其他的了。这就是问题所在。 @TedHopp
    • 它不是在 JLS 的意义上“隐藏”。来自JLS §8.4.8.2(强调添加):“如果一个类C声明或继承了一个static方法m,那么就说m隐藏了任何方法m',其中m的签名是一个子签名(§8.4.2) m' 的签名,在 C 的超类和超接口中,否则 C 中的代码可以访问。” 在你的示例中,Base 中的 doSomethingStatic()仍然可以从Derived 中的代码访问,因为参数不同。什么都没有隐藏。
    【解决方案3】:

    根据 hagubear 有价值的 cmets,似乎语句的作者意味着通过使用具有相同声明的方法重载隐藏方法。

    引用此link:

    我们可以在子类中声明具有相同签名的静态方法,但它 不被视为覆盖,因为不会有任何运行时 多态性。 (...) 如果派生类定义了一个 与基类中的静态方法具有相同签名的静态方法, 派生类中的方法隐藏了基类中的方法。

    因此,在具有完全相同声明的子类中定义方法有效地隐藏了子类中的原始方法。但是,与字段一样,强制转换为父级将恢复原始访问权限。

    示例代码:

    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");
        }
    }
    

    【讨论】:

    • 表达得很好。我比我之前的回答更喜欢这个;)
    【解决方案4】:

    简短的回答是:不,这是不可能的。你混淆了一些术语。 隐藏与可访问性无关(这是你真正要问的,不是可见性,它与范围 和 shadowing 并在 Chapter 6 of the Java Language Specification (JLS) 中讨论)。

    现在是更长的答案。 overriding 一词适用于实例方法,而 hiding 一词适用于类 (static) 方法。来自Java Tutorial topic Overriding and Hiding Methods:

    隐藏静态方法和覆盖实例方法之间的区别具有重要意义:

    • 被调用的重写实例方法的版本是子类中的版本。
    • 被调用的隐藏静态方法的版本取决于它是从超类调用还是从子类调用。

    这里的一些其他答案提供了关于方法隐藏的错误示例,所以让我们回到 JLS,这次是 §8.4.8:

    方法被逐个签名覆盖或隐藏。

    也就是说,要覆盖或隐藏父类中的方法,子类必须定义具有相同签名的方法——基本上,相同数量和类型的参数(尽管泛型和类型擦除使规则比这复杂一点)。还有关于返回类型和throws 子句的规则,但这些似乎与这个问题无关。

    请注意,您可以在子类中定义与父类(或实现的接口)中的方法具有相同名称的方法,但参数的数量或类型不同。在这种情况下,您重载方法名称,既不覆盖也不隐藏任何东西;子类方法是一种新方法,几乎​​独立于继承的方法。 (当编译器必须将方法与方法调用相匹配时,存在交互,但仅此而已。)

    现在回答您的问题:accessibility 和 hiding(以及 visibility)这两个术语是 Java 中的独立概念。正如您所说,有一个“原则”,即子类根本无法减少继承方法的可访问性。无论您是覆盖实例方法还是隐藏类方法,这都适用。来自the JLS §8.4.8.3:

    覆盖或隐藏方法的访问修饰符 (§6.6) 必须提供至少与覆盖或隐藏方法一样多的访问权限,如下所示:

    • 如果覆盖或隐藏的方法是public,那么覆盖或隐藏的方法必须是public;否则,会发生编译时错误。

    • 如果覆盖或隐藏的方法是protected,那么覆盖或隐藏的方法必须是protected或public;否则,会发生编译时错误。

    • 如果被覆盖或隐藏的方法具有默认(包)访问权限,则覆盖或隐藏方法必须不为private;否则,会发生编译时错误。

    总而言之,static 方法可以隐藏这一事实与更改方法的可访问性无关。

    【讨论】:

      猜你喜欢
      • 2018-11-20
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      相关资源
      最近更新 更多