Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Signature difference when hiding static method in a subclass

Tags:

java

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?

like image 256
Paweł Chorążyk Avatar asked Aug 10 '15 22:08

Paweł Chorążyk


1 Answers

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 static method m, then m is said to hide any method m', where the signature of m is a subsignature (§8.4.2) of the signature of m', 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 abstract methods 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 m2 in B overrides or hides a method declaration m1 in A. Then:

  • If m2 has a throws clause that mentions any checked exception types, then m1 must have a throws clause, or a compile-time error occurs.

  • For every checked exception type listed in the throws clause of m2, that same exception class or one of its supertypes must occur in the erasure (§4.6) of the throws clause of m1; 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.

like image 61
Boann Avatar answered Oct 05 '22 04:10

Boann