【问题标题】:Signature difference when hiding static method in a subclass在子类中隐藏静态方法时的签名差异
【发布时间】:2015-08-10 22:10:25
【问题描述】:

最近我在玩一些简单的 Java 代码,使用 main 方法来快速测试我编写的代码。我最终遇到了两个类似的课程:

public class A {
    public static void main(String[] args) {
        // code here
    }
}



public class B extends A {
    public static void main(String[] args) throws IOException {
        // code here
    }
}

我很惊讶代码停止编译并且 Eclipse 抱怨Exception IOException is not compatible with throws clause in A.main(String[])

嗯,这两种方法都是静态的,B 中的main 函数只是隐藏了A 中的一个,所以我认为它们之间完全没有关系。在静态方法中,我们没有多态性,并且在编译期间调用绑定到具体的方法实现,因此我无法理解为什么B 中的main 不能抛出main 签名中未声明的异常A

为什么 Java 设计者决定强制执行这样的约束,如果编译器不强制执行约束,在什么情况下会导致问题?

【问题讨论】:

    标签: java


    【解决方案1】:

    不管怎样,这里是执行此规则的 JLS 的相关部分。

    首先,§8.4.8.2. Hiding (by Class Methods) 给出了适用于此处的方法隐藏的定义:

    如果类 C 声明或继承了 static 方法 m,则称 m 隐藏任何方法 m',其中 m 的签名是子签名(§8.4.2) m' 的签名,在 C 的超类和超接口中,否则 C 中的代码可以访问。

    然后,§8.4.8.3. Requirements in Overriding and Hiding 声明:

    覆盖或隐藏另一个方法的方法,包括实现接口中定义的abstract 方法的方法,不得声明为抛出比被覆盖或隐藏的方法更多的检查异常。

    更准确地说,假设B是一个类或接口,A是B的超类或超接口,B中的方法声明m2覆盖或隐藏了A中的方法声明m1。那么:

    • 如果m2 有一个throws 子句提及任何已检查的异常类型,那么m1 必须有一个throws 子句,否则会发生编译时错误。

    • 对于m2throws 子句中列出的每个检查异常类型,相同的异常类或其超类型之一必须出现在m1throws 子句的擦除(§4.6)中;否则,会发生编译时错误。

    换句话说,错误信息不是编译器的疏忽,也不是对规范的误解; JLS 特别努力提到throws 子句冲突方法隐藏错误(即,使用静态方法)。回到 1.0 的每个 JLS 版本中都有与此等效的语言。

    但是,我无法明确回答您关于为什么在这种情况下存在约束的问题。我无法想象任何需要约束的情况,因为调用静态方法实现的问题总是在编译时完全解决,这与实例方法不同。

    我敢打赌,无论谁首先在 langspec 中设置该约束,都只是过于谨慎,认为阻止某事比允许某事更安全,后来发现它会导致问题。 Java 语言设计是/不是没有其公平份额的缺陷特性(检查异常就是其中之一),这可能是另一个可信的,但这只是一个猜测。

    【讨论】:

      【解决方案2】:

      我从来没有意识到这个事实,但是在准确分析之后,我认为编译器没有理由强制执行这样的约束。虽然没有,但在匹配main 方法时不会出现不一致的情况,因为静态方法的链接也是静态的。在这种情况下不允许覆盖。

      【讨论】:

      • 你的帖子看起来像是对 OP 描述的问题的确认,而不是答案(除非我错过了什么——我住的地方已经很晚了)。
      • 作者问“什么情况下会出问题”,我回答“看不到任何人”。这就是答案。
      • 我明白你的意思,但这似乎仍然不能解释很多,它只是证实了 OP 怀疑不应该有任何问题。所以对我来说,它看起来像“我同意”,这更像是评论而不是答案。
      【解决方案3】:

      关键是我们谈论的是静态方法,它们确实不应该受到多态性的影响。但是,在我看来,静态方法属于 Class 对象,就像任何其他对象一样,它应该服从多态性,对吧?

      【讨论】:

      • 多态性仅适用于非私有、最终或静态的方法。
      • 顺便说一句,这似乎是这个question的副本
      • 这不是重复的,因为我不知道为什么无法覆盖静态方法-我知道静态方法与实例方法完全不同,并且多态在那里不起作用,这是主要的您链接到的主题中的讨论点。
      【解决方案4】:

      'extends' 关键字意味着当你声明 Class B 时,你继承了 Class A 的方法。由于你 Class A 是 Class B 的父类,所以尽管你声明了 main 方法的声明仍然在 Class A 中正在 B 类中重新定义它。

      当你处理静态方法时,父类的方法不会被子类覆盖,它只是被隐藏了。

      http://docs.oracle.com/javase/tutorial/java/IandI/override.html

      可以提供更多细节,但这里是摘录:

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

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


      原答案

      编译器强制执行此约束,以帮助简化更复杂层次结构中的继承。

      以 toString() 方法为例。它被 Java 中的每个对象继承,想象一下如果您能够覆盖该方法的声明以允许子类抛出不同的异常。就处理所有这些潜在的新异常而言,这是一个灾难的秘诀。

      TLDR- 编译器强制你遵守父类函数定义,以便其他程序员可以在晚上睡觉。

      【讨论】:

      • 这里的关键是 main 是一个 static 方法,好像你完全忽略了问题的那一部分。多态性不适用于static 方法。编辑:我记得在 Java 8 中听到了这种变化,为了全面披露,我没有使用过。
      猜你喜欢
      • 1970-01-01
      • 2011-05-04
      • 2012-04-19
      • 2014-12-01
      • 2015-05-31
      • 2011-09-01
      • 1970-01-01
      • 2014-10-04
      • 1970-01-01
      相关资源
      最近更新 更多