ERR01-G: Do Not Throw Exceptions That Are Superclasses of Other Exceptions That a Method May Throw
Introduction
Throwing the superclass of an exception may seem like a convenient way to handle various errors at once, but it can create challenges. While this approach can enhance code robustness, it also introduces complexities during troubleshooting. Throwing root exception classes such as RuntimeException
, Exception
, or Throwable
can remove the ability to determine why the was thrown, making problem diagnosis difficult or even impossible. The higher up in the exception hierarchy you catch the exception, the more obscured information you encounter, leading to frustration and wasted time in error identification.
Lastly, apart from AssertionError
, you should refrain from defining or throwing any subclasses of Error
class. Doing so could introduce unpredictable behaviour and complicate error-handling procedures.
Non-Compliant Code Example
The isName
function in the following non-compliant code example accepts a string and returns "true" if the string consists of two substrings separated by white space, each starting with an uppercase letter. The method also throws a RuntimeException when passing a null string argument.
// Non-compliant Code Example
function isName(s: String) : boolean {
if (s == null) throw new RuntimeException("Null String")
var names : String[] = s.split(" ")
if (names.length != 2) return false
return
(Character.isUpperCase(names[0].codePointAt(0)) &&
Character.isUpperCase(names[1].codePointAt(0)))
}
Compliant Solution
The following compliant solution throws NullPointerException
to denote the specific exceptional condition:
// Compliant Solution
function isName(s : String) : boolean {
if (s == null) throw new NullPointerException()
var names : String[] = s.split(" ")
if (names.length != 2) return false
return
(Character.isUpperCase(names[0].codePointAt(0)) &&
Character.isUpperCase(names[1].codePointAt(0)))
}
The null
check is redundant; if removed, the subsequent call to s.equals("")
would throw a NullPointerException
when s
is null
. More complex code may require explicit testing of invariants and appropriate throw statements.
Compliant Solution
This compliant solution checks that s is not a null pointer by calling the Objects.requireNonNull method:
// Compliant Solution
function isName(s : String) : boolean {
Objects.requireNonNull(s, "s cannot be null!")
var names : String[] = s.split(" ")
if (names.length != 2) return false
return
(Character.isUpperCase(names[0].codePointAt(0)) &&
Character.isUpperCase(names[1].codePointAt(0)))
}
The Objects.requireNonNull
method throws NullPointerException
if s
is a null pointer.
Compliant Solution
This compliant solution checks simply omits an explicit null pointer check:
// Compliant Solution
function isName(s : String) : boolean {
var names : String[] = s.split(" ")
print("s = " + s)
if (names.length != 2) return false
return
(Character.isUpperCase(names[0].codePointAt(0)) &&
Character.isUpperCase(names[1].codePointAt(0)))
}
This solution is acceptable, provided the program fails and no state data is changed before the exception is thrown.
Non-Compliant Code Example
You can pass a non-exception object to the throw statement. If you pass a non-exception object, Gosu first coerces the object to a String
. Next, Gosu wraps the String
in a new RuntimeException
with a message property containing the String
data. Your error-handling code can use the message property for logging or displaying messages to users.
For example, the following code will wrap the string literal "User is not allowed in the bar" in a String object that is used to throw a new RuntimeException:
// Non-compliant Code Example
if (user.Age < 21) {
throw "User is not allowed in the bar"
}
Compliant Solution
This compliant solution throws an IllegalArgumentException, which indicates that a method has been passed using an illegal or inappropriate argument:
// Compliant Solution
if (user.Age < 21) {
throw new IllegalArgumentException("User is not allowed in the bar")
}
Providing direct access to the array objects themselves is safe because String
is immutable.
Exceptions
ERR01-G-EX0: Classes that catch and partially handle exceptions may translate specific exceptions into more general exceptions. This translation could potentially result in throwing RuntimeException
, Exception
, or Throwable
exceptions in some cases.
Risk Assessment
Throwing RuntimeException, Exception, or Throwable exceptions prevents classes from catching the intended exceptions without catching unintended exceptions as well.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR01-G | Low | Highly Likely | Medium | L6 | L2 |
Additional Resources
Was this page helpful?