I am trying to create an IP address in Android from a passed in value (using Bundle), and if it fails I'm creating it using a default IP address that is hard coded. If that fails then I am exiting the app.
What I'd like to know is if its ok to nest try/catch's as I have done here, or is there a better way.
try {
// serverSettings is the Bundle name that was passed in.
ipAddress = InetAddress.getByName(serverSettings.getString("serverIp"));
} catch (UnknownHostException e) {
Log.e("ERROR:", "IOException: Failed to create IP, trying default");
try {
// DEFAULT_IP is the hard-coded default fall-back address
ipAddress = InetAddress.getByName(DEFAULT_IP);
} catch (UnknownHostException e1) {
Log.e("ERROR:", "IOException: Total fail, exiting");
e1.printStackTrace();
finish();
}
}
Although this is sometimes unavailable, nesting try/catch blocks severely impacts the readability of the source code as it makes it difficult to understand which block will catch which exception.
In Java, we can use a try block within a try block. Each time a try statement is entered, the context of that exception is pushed on to a stack. Given below is an example of a nested try.
When a try catch block is present in another try block then it is called the nested try catch block. Each time a try block does not have a catch handler for a particular exception, then the catch blocks of parent try block are inspected for that exception, if match is found that that catch block executes.
Yes, we can define one try block with multiple catch blocks in Java. Every try should and must be associated with at least one catch block.
It's legal Java. It looks clunky to me, and I'd probably do it differently, but it's valid and works.
Here's how I'd do it:
public InetAddress getServerAddress() {
for (String address : new String[] {serverSettings.getString("serverIp"), DEFAULT_IP}) {
try {
return InetAddress.getByName(address);
} catch (UnknownHostException e) {
Log.e("ERROR:", "Cannot resolve " + address);
}
}
Log.e("ERROR:", "Total fail, exiting");
finish();
return null; // not reached
}
I think it's going to be debatable what way is better, but here's another option that some may consider to be a bit more "expressive" and readable, though it's more lines of code:
public InetAddress tryHost(String hostName) {
InetAddress address = null;
try {
address = InetAddress.getByName(hostName);
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
Then in your code, just do:
InetAddress address = null;
address = tryHost(serverSettings.getString("serverIp"));
if (address = null)
address = tryHost(DEFAULT_IP);
if (address = null) {
// handle error, throw exception
}
finish();
Another variation is to set the default first:
ipAddress = null;
try {
// serverSettings is the Bundle name that was passed in.
ipAddress = InetAddress.getByName(DEFAULT_IP); // Set default address
ipAddress = InetAddress.getByName(serverSettings.getString("serverIp")); // Try passed-in address
} catch (UnknownHostException e) {
if (ipAddress == null) {
Log.e("ERROR:", "IOException: Total fail, exiting");
e1.printStackTrace();
finish();
}
}
If the call using the Bundle'd value fails, then the exception is thrown before ipAddress is modified, so ipAddress is already set to the default. Of course, this is only a valid pattern if DEFAULT_IP should always be resolvable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With