gavenkoa
gavenkoa

Reputation: 48753

Emulate goto in Java

I have code (simplified hypothetical example):

public String handleEditRequest(...) {
    if (isBadArg()) {
       ...
       req.getSession().setAttribute("errorMessage", "...");
       return "redirect:/product/edit" + id + "/.htm";
    }
    if (isEntityNotInDataBase()) {
       ...
       req.getSession().setAttribute("errorMessage", "...");
       return "redirect:/error.htm";
    }
    ...
    return "redirect:/product/show" + id + "/.htm";
}

Now I have task to add SLA timing calculation to this code. If I have only one return statement I just wrap method body in:

public String handleEditRequest(...) {
    Timer timer = new Timer().start();
    // ... CODE ...
    slaService.send(timer.stop());
    return "redirect:/product/show" + id + "/.htm";
}

But error handling code block such solution. There are many possibilities. For example add:

timer.stop();
slaService.send(timer);

before any return statement. But this lead to code duplication.

Or use try {} catch () {} (inspired to me by Emacs Elisp experience):

private static class GotoError extends Throwable {}

public String handleEditRequest(...) {
    Timer timer = new Timer().start();
    String redirectUrl = "redirect:/product/show" + id + "/.htm";
    try {
        if (isBadArg()) {
           ...
           req.getSession().setAttribute("errorMessage", "...");
           redirectUrl = "redirect:/product/edit" + id + "/.htm";
           throw new GotoError();
        }
        if (isEntityNotInDataBase()) {
           ...
           req.getSession().setAttribute("errorMessage", "...");
           redirectUrl = "redirect:/error.htm";
           throw new GotoError();
        }
        ...
    } catch (GotoError e) { }
    slaService.send(timer.stop());
    return redirectUrl;
}

But today I rethink and use do { ... break; ... } while (false); paradigm (after googling I found while (true) {if () {break} .... break;} example, but I hate final break statement!!):

public String handleEditRequest(...) {
    Timer timer = new Timer().start();
    String redirectUrl = "redirect:/product/show" + id + "/.htm";
    do {
        if (isBadArg()) {
           ...
           req.getSession().setAttribute("errorMessage", "...");
           redirectUrl = "redirect:/product/edit" + id + "/.htm";
           break;
        }
        if (isEntityNotInDataBase()) {
           ...
           req.getSession().setAttribute("errorMessage", "...");
           redirectUrl = "redirect:/error.htm";
           break;
        }
        ...
    } while (false);
    slaService.send(timer.stop());
    return redirectUrl;
}

What construct to use for goto in error checking code in Java? Do I miss some build-in language constructs that naturally solve problem?

PS

C-like error handling come from my 10 year in plain C and allow to live with linear code. Many Java spaghetti look like:

if (!bad1()) {
    if (!bad2()) {
        if (!bad3()) {
            if (!bad4()) { ... }}}}

when this can be written like:

if (bad1())
   return errorCode;
if (bad2())
    throw new Ex();
if (bad3())
   return null;
if (bad4())
   break;
// Now do job, all fine!

Upvotes: 1

Views: 333

Answers (2)

Jon Skeet
Jon Skeet

Reputation: 1500385

Just wrap the whole method in another one:

public String handleEditRequest(...) {
    Timer timer = new Timer().start();
    try {
        return handleEditRequestImpl(...);
    } finally {
        timer.stop();
        slaService.send(timer);
    }
}

private String handleEditRequestImpl(...) {
    // Lots of code
}

If handleEditRequest is implementing an interface, you could even potentially use a proxy to automatically time all invocations of methods in that interface. (The proxy would implement the interface by handling each method the same way: start timer, invoke the real method, stop timer.)

(As noted elsewhere, it looks like you could quite possibly centralize more of your error handling too...)

Upvotes: 5

Bathsheba
Bathsheba

Reputation: 234685

The obvious thing to do is to put all your error code in a separate function and use return to break out. You can even relay a status or throw an exception.

I use do{...}while(false); in Java a fair bit but it is rather anachronistic.

Upvotes: 2

Related Questions