Some notes on computer stuff

Exception specifiers and finally keyword in Java

October 8, 2012
[programming] [java] [eclipse] [exceptions] [finally] [issue]

Here is part of code I wrote once:

String execute(String data)
{
    String result = null;
    try {
        final Execute execute = new Execute();
        execute.setId(id);
        execute.setData(data);
        result = cmiss.execute(execute).getData();
    } catch (RemoteException e) {
        e.printStackTrace();
    } catch (CommandFault e) {
        e.printStackTrace();
    } finally {
        return result;
    }
}

After adding proper exception handling it becomes this:

String execute(String data)
{
    String result = null;
    try {
        final Execute execute = new Execute();
        execute.setId(id);
        execute.setData(data);
        result = cmiss.execute(execute).getData();
    } catch (RemoteException e) {
        throw new LogicError("Cmiss: " + e.getMessage());
    } catch (CommandFault e) {
        throw new LogicError("Cmiss: " + e.getMessage());
    } finally {
        return result;
    }
}

At this point Eclipse start complaining about the finally block saying that finally block does not complete normally and proposing Add suppressWarnings `finally` to execute() as the only possible solution. While everything was working fine I didn't care about what Eclipse says until one day something went wrong, but code on layers above execute() didn't get any exceptions. This was something I didn't expect at all, so I started the debugger and saw that exception is throws as it should be, but then it gets "swallowed" by the finally block. But wait, the method doesn't have exception list at all, java compiler silently compiles such code because it knows that with finally block containing return statement in it, no exception will be thrown. Unfortunately I didn't know that before.

Now code looks like this and works as expected:

String execute(String data)
    throws LogicError
{
    String result = null;
    try {
        final Execute execute = new Execute();
        execute.setId(id);
        execute.setData(data);
        result = cmiss.execute(execute).getData();
    } catch (RemoteException e) {
        throw new LogicError("Cmiss: " + e.getMessage());
    } catch (CommandFault e) {
        throw new LogicError("Cmiss: " + e.getMessage());
    }
    return result;
}