隐藏静态方法在子类中时的签名差异
最近我用一些简单的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
function只是隐藏A
,所以我认为它们之间完全没有关系。 在静态方法中,我们没有多态性,并且在编译期间调用绑定到具体的方法实现,因此我无法理解为什么B
main
不能抛出未在A
中的main
签名中声明的exception。
为什么Java设计者决定强制执行这样的约束,如果编译器没有强制执行约束,它会在什么情况下导致问题呢?
对于它的价值,这里是执行此规则的JLS的相关部分。
首先, §8.4.8.2。 隐藏(通过类方法)给出了适用于此的方法隐藏的定义:
如果类C声明或inheritance
static
方法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
子句,否则会发生编译时错误。对于
m2
的throws
子句中列出的每个已检查exception类型,在m1
的throws
子句的擦除(第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-编译器强制您遵守父类函数定义,以便其他程序员可以在晚上睡觉。