3

I have the following method, which needs to return a List, but exceptions might occur along the way, and I don't want to handle them in my application, mainly because I don't even know how to handle them. The idea is simple: read data from a CSV file and return the list instantiated using the 'Beans' logic in OpenCSV. The problem is the exceptions. Since I only want the error and stack trace to be printed in the console and the application to be terminated, what is the best approach to deal with this? Should I use throw? Should I use catch? The problem with catch is that the compiler complains that a return is missing, even if I provide it inside the try. This is not for a real application; it's just a simple study project, so if something goes wrong, I just want to know what happened and have the program terminate, nothing more.

public static List<Usuario> ler() {
    try (FileReader reader = new FileReader(UsuarioArquivo.getCaminho().toString())) {
        return new CsvToBeanBuilder<Usuario>(reader)
            .withType(Usuario.class)
            .build()
            .parse()
        ;
    }
}
6
  • 3
    if you catch and dont throw, then you need to return. If you just want to throw then you dont need a try catch, just let it error out
    – Ewan
    Commented Nov 14 at 0:39
  • 4
    "I only want the error and stack trace to be printed in the console and the application to be terminated" - in that case just don't handle it anywhere. This is fine for a quick study project.
    – Bergi
    Commented Nov 14 at 13:34
  • 1
    Are you using a framework like SpringBoot or is this a plain Java application?
    – bob
    Commented Nov 14 at 15:18
  • @bob I’m working on a plain Java application and am facing challenges finding a library to read/write CSV files directly from/into a Bean. Ideally, I need one that, if the file doesn’t exist, automatically writes the header based on the order of the Bean’s attribute declarations—without requiring any strategy classes or annotations. Any recommendations? Tried OpenCSV and didn't work like I want. Commented Nov 14 at 16:47
  • Does it have to use Beans—is that a core part of your project?
    – bob
    Commented Nov 14 at 17:51

5 Answers 5

13

For the purposes of this method that returns a list, just don't catch exceptions. Let it throw.

Unhandled exceptions in your application still need to be handled, but the error handling occurs much deeper in the call stack. Something closer to main() will essentially wrap the entire application in a try/catch block where it simply prints the message and stacktrace to the error output.

static int main(String[] args) {
    try {
        // CSV logic/application logic goes here

        return 0;
    }
    catch (Exception ex) {
        System.err.println(ex.toString());

        return 1;
    }
}

This is a fairly consistent pattern across languages and tech stacks. Something closer to the start of the program catches everything and either logs the problem or prints it to the error output of the host OS. This detail is often hidden from us programmers by application frameworks, but it still exists. If your program doesn't catch the exception and handle it then the Java runtime needs to. And if the runtime cannot, then the host OS needs to, which should terminate the application. And if the host OS doesn't, then... I dunno. Reboot.

I'm just guessing, but since this is an example project for learning purposes, I imagine you don't have a full framework, which means you need to write the global error handling logic. When you don't see a better place, put it in main() or whatever function represents the start of a new thread, but keep the error handling logic simple. At that point you just need to report the problem for debugging purposes, especially for toy projects. If there is no way to recover and continue a use case, terminate the program.

In cases where retrying a use case is desirable, then you need to catch exceptions closer to the logic for that use case. For example, in the controller of an MVC application. This allows you to redraw the UI with an error message affording the user an opportunity to correct their input and try again.

5
  • 3
    "you don't have a full framework, which means you need to write the global error handling logic" - didn't you just say that the Java runtime would take care of it? I expect it'll print the message and stack trace by default as well.
    – Bergi
    Commented Nov 14 at 13:26
  • @Bergi: but the Java runtime will also terminate the program. When you include global error handling logic in your application (for example a console application like the OP is probably using) then you can at least fall back to a menu or something. Otherwise you leave it up to the runtime or OS to decide, and their decision might not be what you want, so adding global error handling allows you to specify how the program recovers or fails. I would prefer that in most cases, even if I only resort to printing the error to stderr. Commented Nov 14 at 13:31
  • 6
    Sure, but the OP wrote that they "only want the error and stack trace to be printed in the console and the application to be terminated", so in this particular "study project" case I think not handling the exception anywhere in the code is just fine.
    – Bergi
    Commented Nov 14 at 13:34
  • True, but the purpose of a "study project" is to learn, often times using trivial use cases. This gives the OP some practice to see how more sophisticated programs handle errors. But I do agree that the simplest solution is to let the runtime print the error and terminate the program. Commented Nov 15 at 2:56
  • 3
    I disagree, that error handling is needed - Java just prints the stacktrace for unhandled exceptions. There is no need for any customization for the task as stated. System.err.println(ex.toString()) is harmful, as it suppresses stacktrace printing.
    – Basilevs
    Commented Nov 15 at 9:30
13

Sometimes the best way to handle an exception is not at all.

Since I only want the error and stack trace to be printed in the console and the application to be terminated, what is the best approach to deal with this?

This looks like a job for a throws declaration. In Java, if you're throwing an (ick) checked exception out of the method then you need to say so in the method signature. FileReader throws a FileNotFoundException which is a checked exception.

public static List<Usuario> ler() throws FileNotFoundException {
    try (FileReader reader = new FileReader(UsuarioArquivo.getCaminho().toString())) {
        return new CsvToBeanBuilder<Usuario>(reader)
            .withType(Usuario.class)
            .build()
            .parse()
        ;
    }
}

Throwing from within a try-with-resources can get interesting but I believe this is all you need here.

This is not for a real application; it's just a simple study project, so if something goes wrong, I just want to know what happened and have the program terminate, nothing more.

Real or not, depending on how you're running the program, just letting the exception go uncaught may already do exactly that. Halting a program that is in an undefined state is a good thing. If you can't recover and put it back into a defined state then it's best to let it halt before it corrupts data, causes a security issue, or sends the president threating emails.

Now maybe you can recover. Either by rejecting the requested task or performing the task in some degraded way. Would your program react well if you simply returned an empty list?

I don't want to handle them in my application, mainly because I don't even know how to handle them.

Well that’s a bad reason to not handle them. Handle them wherever you can see what to do next. Something you were counting on didn’t work out. Can you see what to do next?

Easily 80% of code is just dealing with things going weird. Don’t be surprised if this takes a fair bit of work.

5

Let the exceptions bubble up to your caller, by just adding a throws clause to the ler() method.

If someone calls your ler() method, it's important for them to know whether that was successful, meaning that the List returned here really represents the file contents in the way that was intended (that the method was able to fulfill its contract).

In most cases, this caller called ler() for some good reason, so with ler() failing it cannot complete its job in a meaningful way. So the caller again should signal its failure to its caller, and so on. That's what exceptions give you out-of-the-box (in Java, you need to declare the exception type in a throws clause).

Somewhere up the call stack, there might be a place where you can continue, even if something failed (e.g. a command loop in a console program, the main menu of a UI program, the services level of a web server, ...). There's the place where catching an exception makes sense. E.g. you want your server to continue processing new requests even if one of them couldn't be fullfilled.

Don't pay too much attention to the type of exception. The fact that you receive an exception tells you that some method failed. The type of exception gives you additional information, why it failed - that's nice for debugging, but your application logic should hardly ever try to analyze the failure reason.

7
  • 2
    We MUST pay attention to the Type of Exception! The most important part of Structured Exception Handling is the Handling part, for which you need to know /what/ went wrong and, ideally, /why/. Agreed, your code has no need for "analyzing the failure reason". It's already been handed that because it caught a specific Type of Exception. Then, it just has to do something "useful" with it.
    – Phill W.
    Commented Nov 14 at 8:28
  • 2
    @PhillW. (I know that this has the potential for heated discussions). If a method fails, then I typically fail as well. I can either let the exception bubble up (my preferred way), or wrap it into some other type. I should catch an exception (without re-throw) only if I can turn the method's failure into my success by some "recovery strategy". Can I (as an outsider to the method) devise a viable recovery strategy, better than the method itself can do? Do (and should) I know enough about the inner workings of the method that I can base that strategy on the exception type? Commented Nov 14 at 8:52
  • 1
    "recovery strategy" = "Handling". "Can I ... devise a viable recovery strategy ..." Yes. The called method could not do what was needed. Now the calling method can try /something else/! "Do ... I know enough about the ... method ..." You don't /need/ to. What matters is what's you can "usefully" do with the Exception. "... that I can base that strategy on the exception type?" That's exactly what you're /supposed/ to do. The Exception should give you, the Handler, sufficient information that you can. If it doesn't, you can't handle it, so don't catch it.
    – Phill W.
    Commented Nov 14 at 9:50
  • 1
    @PhillW. We seem to advocate different architectures regarding the role of failures and exceptions. Your architecture is that the exception not only communicates that the method failed and why it failed, but also the information what can be done by an automat to mitigate the failure (e.g. retry). In my architecture a method that can give hints how to make it succeed should take these actions itself before throwing. That's not always possible, but my preferred way. Commented Nov 14 at 10:55
  • We'll agree to disagree. IMHO, you're under-valuing SEH and potentially breaking Separation of Concerns as well, by over-complicating each method with "fallback" code that should be elsewhere (and context-dependent). At least you'll never need to create any of your own Exception [sub-]classes, since you only ever report a "failure".
    – Phill W.
    Commented Nov 15 at 8:23
5

Since I only want the error and stack trace to be printed in the console and the application to be terminated, what is the best approach to deal with this?

If you don't want to handle a checked exception and you don't want to have to add throws SomeCheckedException to every method signature up the stack, the simplest way to achieve that is to wrap your checked exception in an unchecked exception. E.g.:

try {
    // some stuff
} catch (SomeCheckedException e) {
    throw new SomeUncheckedException("a message if desired", e);
}
1

I think a good approach here would be to use a try-catch block in your method to print the stack trace and return null if an exception occurs

Something like this:

import com.opencsv.bean.CsvToBeanBuilder;
import java.io.FileReader;
import java.util.List;
import java.util.ArrayList;

public static List<Usuario> ler() {
    try (FileReader reader = new FileReader(UsuarioArquivo.getCaminho().toString())) {
        return new CsvToBeanBuilder<Usuario>(reader)
            .withType(Usuario.class)
            .build()
            .parse();
    } catch (Exception e) {
        e.printStackTrace(); // Print the exception and stack trace to the console
        System.exit(1); // Terminates the program with a non-zero status
    }
    return new ArrayList<>(); // Added to satisfy compiler, although System.exit will terminate
}
1
  • Why catch? echo 'void main() { throw new RuntimeException();}' > /tmp/test.java ; java --enable-preview --source 22 /tmp/test.java ;;; Exception in thread "main" java.lang.RuntimeException at test.main(test.java:1)
    – Basilevs
    Commented Nov 15 at 9:35

Not the answer you're looking for? Browse other questions tagged or ask your own question.