Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

return in try block vs return after the block

I have a try statement in a small static method, is there a best practice as to where I should return from?

try {
    mightThrow();
    return true;
} catch (Exception e) {
    return false;
}

or after,

try {
    mightThrow();
} catch (Exception e) {
    return false;
}
return true;

Functionally, these should preform identically, is there actually a bytecode difference? Performance wise, are they the exact same?

Or is one just preferred over the other? Which and why?

like image 924
Matt Clark Avatar asked Aug 04 '15 02:08

Matt Clark


People also ask

Should return be inside try block?

Putting return where it makes sense improves readability and maintainability and makes your code simpler to understand. You shouldn't care as finally block will get executed if a return statement is encountered. Save this answer.

What happens if we have return statement in try block?

When you will try to execute the preceding program, you will get an unreachable code error. Key point: Any statement after return statement will result in compile-time error stating “Unreachable code”.

Can we use return statement in try catch or finally block?

In a try-catch-finally block that has return statements, only the value from the finally block will be returned. When returning reference types, be aware of any updates being done on them in the finally block that could end up in unwanted results.

Can we have return statement after finally block?

Yes you can write the return statement in a finally block and it will override the other return value. The output is always 2, as we are returning 2 from the finally block.


2 Answers

I haven't heard of an actual best practice in this matter, but you often see that when methods use premature returns, the returns true case is at the bottom, e.g.

public bool canReadFile(path) {
  if (!fileExists(path))  
    return false;

  if (!fileIsReadable(file))
    return false;

  ...
  return true;
}

Because of that, I'd suggest you follow this practice for try/catch blocks. It also makes it quicker to see what the "expected" return value is.

In regards to the bytecode, then yes, there is indeed a difference. I made a quick sample program

class TryBlock {
    public static void main(String[] args) {
        a();
        b();
    }

    public static boolean a() {
        try {
            System.out.println("A");
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean b() {
        try {
            System.out.println("B");
        } catch (Exception e) {
            return false;
        }
        return true;
    }

}

Then compiled it and inspected the bytecode

$ javac TryBlock.java; javap -c TryBlock
Compiled from "TryBlock.java"
class TryBlock {
  TryBlock();
    Code:
       0: aload_0
       // Method java/lang/Object."<init>":()V
       1: invokespecial #1                  
       4: return

  public static void main(java.lang.String[]);
    Code:
       // Method a:()Z
       0: invokestatic  #2                  
       3: pop
       // Method b:()Z
       4: invokestatic  #3                  
       7: pop
       8: return

  public static boolean a();
    Code:
       // Field java/lang/System.out:Ljava/io/PrintStream;
       0: getstatic     #4                  
       // String A
       3: ldc           #5                  
       // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       5: invokevirtual #6                  
       8: iconst_1
       9: ireturn
      10: astore_0
      11: iconst_0
      12: ireturn
    Exception table:
       from    to  target type
           0     9    10   Class java/lang/Exception

  public static boolean b();
    Code:
       // Field java/lang/System.out:Ljava/io/PrintStream;
       0: getstatic     #4                  
       // String B
       3: ldc           #8                  
       // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       5: invokevirtual #6                  
       8: goto          14
      11: astore_0
      12: iconst_0
      13: ireturn
      14: iconst_1
      15: ireturn
    Exception table:
       from    to  target type
           0     8    11   Class java/lang/Exception
}

So is there a performance difference? While I haven't tested, my bet is there won't be anything noticeable. On top of that, this will hardly be the bottleneck of your application.

like image 194
kba Avatar answered Sep 23 '22 22:09

kba


To me this is more a semantics and readability issue.

If your return true is at the end of the segment outside of the try/catch block, it means this function is supposed to return true value unless anything bad happened in between which interrupts the normal flow.

Instead if the return true is at the end of the try block, it means the function should only return true if all the attempts in the try block succeeded.

This difference in byte code is more or less neglectable; and I agree with @kba this is more a style issue. Many return statement at different places inside deeply embedded if blocks would usually be confusing; so it's a good practice to make your code less complex. I.e.

  1. Use flat blocks than deep embed where possible
  2. Carry less state to another code segment and/or an inner if, for or try block
  3. Use less state and flow controlling variable
  4. Use less statement like return or go because they are more or less a compulsory change of the logic flow.

Hope that helps.

like image 32
Alex Suo Avatar answered Sep 25 '22 22:09

Alex Suo