user93353
user93353

Reputation: 14039

Changing the interface without recompiling the implementing class

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)

Upvotes: 7

Views: 1660

Answers (1)

Brian
Brian

Reputation: 17309

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.

Upvotes: 4

Related Questions