Recently I was playing around with some simple Java code using main methods to quickly test the code I wrote. I ended up in a situation where I had two classes similar to those:
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
}
}
I was quite surprised that the code stopped compiling and Eclipse complained that Exception IOException is not compatible with throws clause in A.main(String[]).
Well, both methods are static and the main function in B is just hiding the one from A, so I thought that there is completely no relation between them. In static methods we have no polymorphism and the call is bound to the concrete method implementation during the compilation, therefore I cannot understand why main in B cannot throw exception that is not declared in main signature in A.
Why Java designers decided to enforce a constraint like this and in what situations it would cause problems if the constraint was not enforced by the compiler?
For what it's worth, here is the relevant portion of the JLS that enforces this rule.
First, §8.4.8.2. Hiding (by Class Methods) gives a definition for method hiding that applies here:
If a class C declares or inherits a
staticmethodm, thenmis said to hide any methodm', where the signature ofmis a subsignature (§8.4.2) of the signature ofm', in the superclasses and superinterfaces of C that would otherwise be accessible to code in C.
Then, §8.4.8.3. Requirements in Overriding and Hiding states that:
A method that overrides or hides another method, including methods that implement
abstractmethods defined in interfaces, may not be declared to throw more checked exceptions than the overridden or hidden method.More precisely, suppose that B is a class or interface, and A is a superclass or superinterface of B, and a method declaration
m2in B overrides or hides a method declarationm1in A. Then:
If
m2has athrowsclause that mentions any checked exception types, thenm1must have athrowsclause, or a compile-time error occurs.For every checked exception type listed in the
throwsclause ofm2, that same exception class or one of its supertypes must occur in the erasure (§4.6) of thethrowsclause ofm1; otherwise, a compile-time error occurs.
In other words, the error message is not some oversight in the compiler, or a misinterpretation of the spec; the JLS makes the specific effort to mention that throws clause conflicts are an error with method hiding (i.e., with static methods). There is equivalent language to this in every version of the JLS back to 1.0.
However, I can't definitively answer your question of why the constraint is present in this case. I can't conceive of any situation in which the constraint is necessary, since the issue of which static method implementation is invoked is always completely resolved at compile-time, unlike for instance methods.
I'd bet a small amount of money that whoever first put that constraint in the langspec was simply being over-cautious, figuring it was safer to prevent something than to allow it and then later discover it causes problems. The Java language design is/was not without its fair share of flawed features (checked exceptions being one of them), and this could credibly be another, but this is just a guess.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With