Requirements
The Requirements trait that contains require
, and requireState
, and requireNonNull
methods for checking pre-conditions
that give descriptive error messages extracted via a macro.
These methods of trait Requirements
aim to improve error messages provided when a pre-condition check fails at runtime in
production code. Although it is recommended practice to supply helpful error messages when doing pre-condition checks, often people
don't. Instead of this:
scala> val length = 5
length: Int = 5
scala> val idx = 6
idx: Int = 6
scala> require(idx >= 0 && idx <= length, "index, " + idx + ", was less than zero or greater than or equal to length, " + length)
java.lang.IllegalArgumentException: requirement failed: index, 6, was less than zero or greater than or equal to length, 5
at scala.Predef$.require(Predef.scala:233)
...
People write simply:
scala> require(idx >= 0 && idx <= length)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:221)
...
Note that the detail message of the IllegalArgumentException
thrown by the previous line of code is simply, "requirement failed"
.
Such messages often end up in a log file or bug report, where a better error message can save time in debugging the problem.
By importing the members of Requirements
(or mixing in its companion trait), you'll get a more helpful error message
extracted by a macro, whether or not a clue message is provided:
scala> import org.scalactic._
import org.scalactic._scala> import Requirements._
import Requirements._
scala> require(idx >= 0 && idx <= length)
java.lang.IllegalArgumentException: 6 was greater than or equal to 0, but 6 was not less than or equal to 5
at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56)
...
scala> require(idx >= 0 && idx <= length, "(hopefully that helps)")
java.lang.IllegalArgumentException: 6 was greater than or equal to 0, but 6 was not less than or equal to 5 (hopefully that helps)
at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56)
...
The requireState
method provides identical error messages to require
, but throws
IllegalStateException
instead of IllegalArgumentException
:
scala> val connectionOpen = false
connectionOpen: Boolean = falsescala> requireState(connectionOpen)
java.lang.IllegalStateException: connectionOpen was false
at org.scalactic.Requirements$RequirementsHelper.macroRequireState(Requirements.scala:71)
...
Thus, whereas the require
methods throw the Java platform's standard exception indicating a passed argument
violated a precondition, IllegalArgumentException
, the requireState
methods throw the standard
exception indicating an object's method was invoked when the object was in an inappropriate state for that method,
IllegalStateException
.
The requireNonNull
method takes one or more variables as arguments and throws NullPointerException
with an error messages that includes the variable names if any are null
. Here's an example:
scala> val e: String = null
e: String = nullscala> val f: java.util.Date = null
f: java.util.Date = null
scala> requireNonNull(a, b, c, d, e, f)
java.lang.NullPointerException: e and f were null
at org.scalactic.Requirements$RequirementsHelper.macroRequireNonNull(Requirements.scala:101)
...
Although trait Requirements
can help you debug problems that occur in production, bear in mind that a much
better alternative is to make it impossible for such events to occur at all. Use the type system to ensure that all
pre-conditions are met so that the compiler can find broken pre-conditions and point them out with compiler error messages.
When this is not possible or practical, however, trait Requirements
is helpful.
Next, learn about Snapshots.