Avoid Breaking the Cause Chain

 class​ TransmissionParser {
 static​ Transmission parse(String rawMessage) {
 if​ (rawMessage != ​null
  && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
 throw​ ​new​ IllegalArgumentException(
  String.format(​"Expected %d, but got %d characters in '%s'"​,
  Transmission.MESSAGE_LENGTH, rawMessage.length(),
  rawMessage));
  }
 
  String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
  String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
 try​ {
 int​ id = Integer.parseInt(rawId);
  String content = rawContent.trim();
 return​ ​new​ Transmission(id, content);
  } ​catch​ (NumberFormatException e) {
»throw​ ​new​ IllegalArgumentException(
  String.format(​"Expected number, but got '%s' in '%s'"​,
  rawId, rawMessage));
  }
  }
 }

Exceptions can cause more exceptions. When an exception is caught but cannot be handled, it must be rethrown. If a bug is involved, this can propagate until the program crashes. If the error handling was done right, you will be presented with a stack trace that represents the cause chain—a list where each exception is linked to the one that caused it. When tracking down a bug, a detailed cause chain is worth a pile of gold.

That’s why you need to make sure that you never break this chain. For instance, look at the code above. It catches the NumberFormatException and throws a new IllegalArgumentException with an informative message instead. No bad exception handling as such—but it breaks the cause chain!

Can you spot the problem? It’s the IllegalArgumentException not getting the reference to its cause, the NumberFormatException. Without this link there’s no cause chain. When you look at the stack trace of this IllegalArgumentException, you won’t find a hint that it stems from a NumberFormatException or from what line in the code. We lost a lot of useful information—the NumberFormatException has its own message that provides more information and context on another level of abstraction and its own stack trace with line numbers.

So how can we keep the cause chain instead of breaking it?

 class​ TransmissionParser {
 static​ Transmission parse(String rawMessage) {
 if​ (rawMessage != ​null
  && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
 throw​ ​new​ IllegalArgumentException(
  String.format(​"Expected %d, but got %d characters in '%s'"​,
  Transmission.MESSAGE_LENGTH, rawMessage.length(),
  rawMessage));
  }
 
  String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
  String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
 try​ {
 int​ id = Integer.parseInt(rawId);
  String content = rawContent.trim();
 return​ ​new​ Transmission(id, content);
  } ​catch​ (NumberFormatException e) {
»throw​ ​new​ IllegalArgumentException(
  String.format(​"Expected number, but got '%s' in '%s'"​,
  rawId, rawMessage), e);
  }
  }
 }

Exceptions have various constructors and some of them allow us to pass in a Throwable as a cause. By passing in a Throwable, we link an exception to its cause, thereby building the cause chain. We recommend that you use the constructor Exception(String message, Throwable cause) and provide a message as well.

We’ve seen many kinds of broken cause chains in actual code. Take a look at the worst of all:

 } ​catch​ (NumberFormatException e) {
 // BAD! Cause chain interrupted!
 throw​ ​new​ IllegalArgumentException(e.getCause());
 }

It seems okay. The throw provides e.getCause() as an input parameter and creates a cause chain, but it removes one exception from it, namely the NumberFormatException because it only links its cause, but not the exception itself. That’s worse than no cause chain, because leaving out selected parts can mislead you.

So if you need to throw an exception within a catch block, just pass in a message and the caught exception as a cause directly:

 throw​ ​new​ IllegalArgumentException(​"Message"​, e);
..................Content has been hidden....................

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