隐藏静态方法在子类中时的签名差异

最近我用一些简单的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[])

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

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

对于它的价值,这里是执行此规则的JLS的相关部分。

首先, §8.4.8.2。 隐藏(通过类方法)给出了适用于此的方法隐藏的定义:

如果类C声明或inheritancestatic方法m ,则m被称为隐藏任何方法m' ,其中m的签名是m'的签名的子签名(第8.4.2节),在超类和超接口中。否则C中的代码可以访问的C.

然后, §8.4.8.3。 覆盖和隐藏的要求指出:

覆盖或隐藏另一个方法的方法(包括实现接口中定义的abstract方法的方法)可能不会被声明为抛出比重写或隐藏方法更多的已检查exception。

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

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

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

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

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

我敢打赌少量资金,无论谁先把这种限制放在langspec中,都只是过于谨慎,认为防止某些东西比允许它更安全,然后发现它会导致问题。 Java语言设计不是没有公平分享有缺陷的function(检查exception就是其中之一),这可能是另一个,但这只是一个猜测。

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

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

‘extends’关键字意味着在声明类B时inheritanceA类中的方法。由于类A是类B的父类,因此主方法的声明仍然在A类中,尽管您正在重新定义它在Bclass

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

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

可以提供更多细节,但这里有一个摘录:

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

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


原始答案

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

以toString()方法为例。 它由Java中的每个对象inheritance,假设您能够覆盖此方法的声明以允许子类抛出不同的exception。 在处理所有这些潜在的新例外方面,这是一个灾难的处方。

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