29

I was wondering why the following code would be accepted by the Java compiler:

public class Main {

    public static void main(String ... args){
        System.out.println("a() = " + a());
    }

    public static String a (){
        try {
            return "a";
        }catch(Throwable t){
        }finally{
            return "b";
        }
    }
}

This can and should not work. The java specification states the finally block will always be executed, but at the same time the return value has already been specified. So either you cannot execute the return "b" statement, because you have exited at return "a", which would be incorrect.

However, the other option is that you execute the return "b" statement, and thereby totally disregarding the return "a" statement...

I would say that both are wrong, and I would expect that this does not compile. However it compiles and runs fine. I will leave the answer as a nice exercise to the reader ;).

Basically my question is: Besides being bad practice, would this be considered a Java bug, or does this have other wonderful uses other than obfuscation?

Edit:

The question is not so much if it is a bug, that has been answered, but does it have nice use cases?

Maroun
  • 94,125
  • 30
  • 188
  • 241
Yuri
  • 2,008
  • 17
  • 36
  • 2
    The question is one of the most interesting questions I have read this week. However, to my understanding, the `try` block can never throw an exception. – Codor Dec 17 '14 at 14:02
  • 1
    Very unexpected output !! – Hichamov Dec 17 '14 at 14:03
  • 4
    Question has *a lot* of duplicates. http://stackoverflow.com/questions/65035/does-finally-always-execute-in-java http://stackoverflow.com/questions/4185340/java-try-finally-return-design-question http://stackoverflow.com/questions/19899155/try-catch-block-return-with-finally-clause-in-java ... – Christophe De Troyer Dec 17 '14 at 14:04
  • 2
    A finally block has the right to override any return value. So your code should return "b". – Andy Reimann Dec 17 '14 at 14:04
  • 3
    @Hichamov What is unexpected in this output? – Tom Dec 17 '14 at 14:05
  • @Codor you are correct. I've just tried this and IntelliJ reports "'finally' block can not complete normally" and "'return' inside 'finally' block: While occasionally intended, such return statements may mask exceptions thrown, and tremendously complicate debugging." – Marv Dec 17 '14 at 14:05
  • @Marv actually, my intellij doesn't warn me at all. – Yuri Dec 17 '14 at 14:06
  • I think it is interesting question, i never heard about anyone use return in finally block at all. Personally i want consider this as bug, more as bad practice. Other thing is, as i remember finally block is not always executed, if i remeber if you call `System.exit` it want be executed – user902383 Dec 17 '14 at 14:07
  • @ChristopheDeTroyer most of your cited duplicates do not mention return values – Yuri Dec 17 '14 at 14:08
  • 1) "most of" suffices for duplicates. 2) They all explain the semantics. No need for this question. – Christophe De Troyer Dec 17 '14 at 14:08
  • @Codor interesting. I get [three warnings](http://i.imgur.com/cIhDmru.png), maybe you have disabled some that are usually enabled or the other way around... – Marv Dec 17 '14 at 14:09
  • @ChristopheDeTroyer please cite me a question which asks for a use case :-). Modified my question to emphasise that part. – Yuri Dec 17 '14 at 14:11
  • @Yuri - Asking for a use-case makes this off-topic, as it tends to attract opinionated answers, which is a different reason to close. This is a Q&A website - there should be definitive answers to questions, not opinions. I'm voting to leave this closed. – ArtOfWarfare Dec 17 '14 at 15:42

1 Answers1

20

Everything works exactly as expected, no bugs here. When you have doubts, the JLS is your savior:

JLS - 14.20.2. Execution of try-finally and try-catch-finally:

If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice:

  • If the finally block completes normally, then the try statement
    completes abruptly for reason R.

  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is
    discarded).

It overrides the value in the try block.

return inside finally discards all exceptions that can be thrown in try clause.

Maroun
  • 94,125
  • 30
  • 188
  • 241
  • Ok, so no bug. Thanks, but what about a **use-case** – Yuri Dec 17 '14 at 14:07
  • @Yuri I actually cannot think of a situation where this is useful. – Maroun Dec 17 '14 at 14:09
  • So in that case it would just be a weird design decision :-) – Yuri Dec 17 '14 at 14:10
  • @Yuri Think of a situation where you have `something = null; try { run = tryThis(); } finally { cleanThings(); return something; }` It might be useful if you want to return `null` as a default value if something bad happened.. – Maroun Dec 17 '14 at 14:33
  • @MarounMaroun If something bad happend? It would always return null. – Tom Dec 17 '14 at 14:38
  • @Tom You're right, but it won't cleanup things.. Maybe I'm over trying to find useful case ;) – Maroun Dec 17 '14 at 14:39
  • @MarounMaroun Well, a use case is, that a return in `finally` would always return a specific value, no matter if an exception occurred or not. So you don't have to write the return statement in the `try`and every `catch` block. But I don't think that this was the main reason for this design decision. – Tom Dec 17 '14 at 14:43
  • think about this one: `int a=3; try{ return ++a; }catch(..){}finally{ return ++a; }`, isn't this awesome? – Yuri Dec 17 '14 at 14:54
  • 1
    @Yuri Why would this be useful? – Maroun Dec 17 '14 at 14:57
  • 1
    @Yuri what is awesome about that? – Tom Dec 17 '14 at 14:58
  • @Tom it returns 5 ;) – Yuri Dec 17 '14 at 14:58
  • @Yuri `int a=3; return a+=2;` does that too. – Tom Dec 17 '14 at 15:01
  • 1
    @Tom you're not very good at code obfuscation, are you? ;-). Just kidding, I was being somewhat sarcastic as to the utter uselessness of this construction :-). – Yuri Dec 17 '14 at 15:02