7

I have the following classes

public abstract interface X 
{
    public abstract void f() throws java.io.IOException;
}


public class Y implements X 
{
    public void f() throws java.io.IOException 
    {
        throw new java.ioIOException("Hello");
    }

    public static void main(String [] args)
    {
        X x = new Y();
        try
        {
            x.f();
        }
        catch (IOException e)
        {
            System.out.println("Caught");
        }
    }

}

Now I compile both and get X.class and Y.class.

Now I change X to remove the throws

public abstract interface X 
{
    public abstract void f();
}

Obviously if I recompile both X & Y, Y's compilation will fail

Y.java:4: f() in Y cannot implement f() in X; overridden method does not throw j
ava.io.IOException

However, what I if only recompile X.java & keep my Y.class which was compiled with the older X.java.

What happens in such a case - is it well defined?

Or does it fall under the category of undefined - i.e. anything can happen?

Are there any guarantees at all - i.e. if I am running it always under Java 1.6.32 under Windows, can I rely on nothing bad happening?

Update: Updated it because some answers said I would get a IncompatibleClassChangeError at runtime. But I don't.

Steps

1) Compile both X.java and Y.java as given above. Run Y.

Output: Caught

2) Change X.java to comment out the throws. Recompile X.java. Do not recompile Y.java.

Run Y

Output: Caught

I am running java on Windows 7

Compiler

javac 1.6.0_35

Runtime

java version "1.6.0_35"
Java(TM) SE Runtime Environment (build 1.6.0_35-b10)
Java HotSpot(TM) Client VM (build 20.10-b01, mixed mode, sharing)
user93353
  • 13,733
  • 8
  • 60
  • 122
  • 1. Why don´t you just try? 2. I´d say the "excess" throws in the implementor yields a compile-time error message, too. – TheBlastOne Feb 06 '13 at 16:49
  • 1
    @TheBlastOne I did try. It works fine. May be you haven't read my post properly - the compile time error will happen only if I recompile Y. – user93353 Feb 06 '13 at 17:17

1 Answers1

4

This is a limitation in Java right now. Create a sub-interface that extends your current interface and override the method without the exception if you really need it. Generally speaking, this is called a "binary change" and results in broken linking when the code runs, and is well-defined in the JLS (there's an entire chapter on it, JLS 13, you specifically want JLS 13.5).

Edit: after further investigation, it turns out I was wrong. From JLS 13.4.21:

Changes to the throws clause of methods or constructors do not break compatibility with pre-existing binaries; these clauses are checked only at compile time.

However, I'd still recommend not doing this since it means a checked exception can essentially become unchecked at runtime.

Brian
  • 17,079
  • 6
  • 43
  • 66
  • The Java 8 `default` won't help here. Removing the exception is not creating a new method. It is changing an existing method in a way that would be inconsistent with its interface. – Stephen C Feb 06 '13 at 17:21
  • @StephenC Turns out I was completely wrong anyway, see my edit. – Brian Feb 06 '13 at 17:30
  • Thanks, Brian - so it looks like it's not very dangerous for now. – user93353 Feb 06 '13 at 17:35
  • @user93353 Correct. You should document that older versions of the interface may throw the exception in your javadocs. That way if you or anyone else gets an uncaught `IOException`, you remember that this change was made and can understand why. – Brian Feb 06 '13 at 17:38