Avoid NullPointerException in Conditionals

 class​ Logbook {
 
 void​ writeMessage(String message, Path location) ​throws​ IOException {
»if​ (Files.isDirectory(location)) {
 throw​ ​new​ IllegalArgumentException(​"The path is invalid!"​);
  }
»if​ (message.trim().equals(​""​) || message == ​null​) {
 throw​ ​new​ IllegalArgumentException(​"The message is invalid!"​);
  }
  String entry = LocalDate.now() + ​": "​ + message;
  Files.write(location, Collections.singletonList(entry),
  StandardCharsets.UTF_8, StandardOpenOption.CREATE,
  StandardOpenOption.APPEND);
  }
 }

The first exception that beginners in Java typically see is the NullPointerException. It’s triggered if you call a method or access an attribute on a reference that is null. To prevent such problems, you should validate method arguments. But be sure to do so in the right order!

The Logbook in this code snippet writes messages into a file. We organize log messages into specific files on the filesystem, which we indicate by the location argument. It’s important that messages are correct, so we need to perform parameter validation.

The current version of the method already performs some validation. Unfortunately, it has two serious problems. First of all, the method doesn’t check for null references properly. If location is null, the call to Files.isDirectory() will fail with an undocumented NullPointerException. The same happens for the second condition if message is null, because we check for message.equals("") first.

When you validate arguments, you have to mind the order: first check for null and only then for domain-specific “illegal” values. We recommend that you check for common default values like empty strings or empty lists first, and only then conduct more specific checks.

Passing null into a method as an argument is really bad style—it means that this method can also function without this parameter. If this is indeed the case, refactor the method into two: one with the parameter and one without. Be aware that a parameter being null usually indicates a programming error on the calling side.

Here’s what a proper validation would look like.

 class​ Logbook {
 
 void​ writeMessage(String message, Path location) ​throws​ IOException {
»if​ (message == ​null​ || message.trim().isEmpty()) {
 throw​ ​new​ IllegalArgumentException(​"The message is invalid!"​);
  }
»if​ (location == ​null​ || Files.isDirectory(location)) {
 throw​ ​new​ IllegalArgumentException(​"The path is invalid!"​);
  }
 
  String entry = LocalDate.now() + ​": "​ + message;
  Files.write(location, Collections.singletonList(entry),
  StandardCharsets.UTF_8, StandardOpenOption.CREATE,
  StandardOpenOption.APPEND);
  }
 }

We’ve resolved all issues in the new version: First, we check all arguments for null values. After that, we check for domain-specific restrictions.

Also, we’ve changed the order of the checks to reflect the order of the arguments in the method signature. This is a good practice, since a proper ordering of parameter validations improves the flow of reading. You’re also less likely to forget to validate one of the parameters. Finally, we’ve used a built-in method to check if a String is empty.

You might ask yourself: Is this level of parameter validation always necessary? It’s not.

As a rule of thumb, you need parameter validation for public, protected, and default methods. That’s because any part of the code can access them, and you might not control how this happens.

In contrast, when you build private methods, you’re able to ensure that you never pass null into them. So in this case, you don’t need to perform as much validation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.179.59