Explain Cause in Message

 class​ TransmissionParser {
 static​ Transmission parse(String rawMessage) {
 if​ (rawMessage != ​null
  && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
»throw​ ​new​ IllegalArgumentException();
  }
 
  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(​"Bad message received!"​);
  }
  }
 }

Exception handling is not only about catching exceptions, but also about throwing them. When throwing an exception, you should follow type conventions to make handling of the exception easier.

The type of an exception already describes what is wrong: bad parameters of a method cause an IllegalArgumentException; reading a nonexistent file causes a FileNotFoundException. But the type alone isn’t enough because it lacks the context: which parameter is wrong and why? Which file isn’t available on the filesystem?

We can only fix bugs that we can reproduce. Otherwise, we don’t know whether we really fixed them. Typically, we start with the stack trace of an exception and trace our way back in the code until we find the root cause. If the exception itself provides a detailed context, this is much easier.

The exceptions thrown in the code above, however, lack context. The first IllegalArgumentException is created with the default constructor and provides no context at all. The second one comes with a message as input for the constructor, but it’s not really helpful. Sadly, messages of this type are very common.

We suggest giving them a bit more thought.

So how can we include the context within the message of the exceptions?

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

Instead of missing or useless information, we provide a triplet: what we expect, what we got, and the overall context. A developer who traces an exception to its origin will find the source much faster with such detailed information.

What’s more, we can reproduce the situation that causes the exception more easily. Even better, we can reuse such triples as test cases—we just need to convert them into JUnit tests to drive a bug fix and act as regression tests later on. We’ll get to testing in the next chapter.

You probably already noticed: we use a template for the message of an exception with Favor Format Over Concatenation in the form of Expected [EXPECTED], but got [ACTUAL] in [CONTEXT]. It forces us to include information about what was expected (e.g., 140 characters) and what we got (e.g., 3 characters) and provides the context (e.g., message “abc”). For instance: Expected 140, but got 3 characters in message ’abc’. You can use any format you like, of course. For us, this one really pays off—when (not if) exceptions occur in production.

But we’re not done yet. Sometimes, exceptions can’t be handled directly. Instead, you have to rethrow or convert them into a more general exception type. When you do, stay clear of the pitfalls we’ll explain in the next comparison: Avoid Breaking the Cause Chain.

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

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