简体   繁体   中英

Is it acceptable practice in Java to nest try/catch statements?

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();
    }
}

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.

It's OK. You could also use a boolean flag which gets turned on in the 1st catch, so you execute the request by IP outside the catch, if your boolean flag is turned on.

boolean failed = false;
try {
    // serverSettings is the Bundle name that was passed in.
    ipAddress = InetAddress.getByName(serverSettings.getString("serverIp"));
} catch (UnknownHostException e) {
    failed = true;
    Log.e("ERROR:", "IOException: Failed to create IP, trying default");    
}

if(failed){
    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();
        }
    }
}

I prefer this. It's a bit cleaner and doesn't involve extra flags.

InetAddress ipAddress = null;
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");
}
if(ipAddress==null){
    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();
    }
}

Yet another variation I like when working through a larger set of tests (on a first-wins basis):

ipAddress = null;
// serverSettings is the Bundle name that was passed in.
String[] addresses = { DEFAULT_IP,
                       serverSettings.getString("serverIp") };

int i = 0;
do {
    try {
        ipAddress = InetAddress.getByName(addresses[i]);
    } catch (UnknownHostException e) {
        Log.e("ERROR:", "IOException: Failed to create IP, trying next");
    }
} while (ipAddress == null && i < addresses.length);

if (ipAddress == null) {
    Log.e("ERROR:", "IOException: Total fail, exiting");
    e1.printStackTrace();
    finish();
}

This is probably more appropriate in my normal use case (looping through SimpleDateFormats to match 3rd-party date strings)

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