简体   繁体   中英

local variable cannot be defined as final due to catching exception while initializing it

I usually like to declare my local variables as final but I see repeating patterns where I cannot do this when I have to handle an exception during its initialization.

For ex I have this code:

final Client myClient = library.getClient("service");
//do a bunch of steps with myClient

But I end up modifying it very often like this:

Client myClient = null;
try {
    myClient = library.getClient("service");
} catch (someException ex) {
    // handle
    throw ex;
}
// do stuff with myClient

I do not want to add the catch after I do all the stuff as it becomes a bit unreadable as it is towards the end. So is there a better way to do this?

You can put the try/catch in a separate method:

. . .
    final Client myClient = getClient();

private Client getClient() {
    try {
        return library.getClient("service");
    catch (someException ex) {
        // handle
        return null;
    }
}

If it doesn't make sense for the processing to continue in the method that calls getClient() , then the other option is to declare it to throw the exception and get rid of the try/catch block.

Put the code in the constructor:

final Client myClient;

public MyClass() throws SomeException {
    myClient = library.getClient("service");
}

For that particular case, you can use a blank final :

final Client myClient;  // Note the lack of initialization here!
try {
    myClient = library.getClient("service");
} catch (someException ex) {
    // handle
    throw ex;
}
// do stuff with myClient

You don't have to initialize a final variable when you declare it, as long as it's initialized exactly once on each code path where it might end up being used.

This only works if your catch block terminates the method (via return or another throw ) — you can't assign some other fallback value to myClient , because it might've been assigned in the try block already, violating the single-initialization rule.

Blank finals are also useful for initializing something in different ways depending on a condition:

final Client myClient;
if (someCondition) {
    myClient = foo();
}
else {
    myClient = bar();
}
// do stuff with myClient

Sure, there are at least two trivial options:

  • You can wrap in RuntimeException s and decide when/if you want to handle it.
  • You can refactor appropriately and move exception handling out of the mainline code.

Both have advantages and disadvantages.

you could split the different parts of your code in methods and hand over the myClient as a final var... you could also add all the code in one try an catch block and throw and handle different exceptions. this is just possible, if you do not need the var in the exception-part... global vars could be also a solution, but not a nice one... there is a ground role. if you can't read your code, split it in methods and classes

As an alternative to wrapping the getClient code (mentioned elsewhere), you can wrap the doStuff code:

try {
    doStuff( library.getClient("service") );
} catch (someException ex) {
    // handle
    throw ex;
}

public void doStuff(final Client myClient) {
    // do stuff
}

Or you could combine both approaches:

try {
    doStuff( getClient() );
} catch (someException ex) {
    // handle
    throw ex;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM