Spec-Zone .ru
спецификации, руководства, описания, API

Programming With Assertions

An assertion is a statement in the JavaTM programming language that enables you to test your assumptions about your program. For example, if you write a method that calculates the speed of a particle, you might assert that the calculated speed is less than the speed of light.

Each assertion contains a boolean expression that you believe will be true when the assertion executes. If it is not true, the system will throw an error. By verifying that the boolean expression is indeed true, the assertion confirms your assumptions about the behavior of your program, increasing your confidence that the program is free of errors.

Experience has shown that writing assertions while programming is one of the quickest and most effective ways to detect and correct bugs. As an added benefit, assertions serve to document the inner workings of your program, enhancing maintainability.

This document shows you how to program with assertions. It covers the topics:


Introduction

The assertion statement has two forms. The first, simpler form is:

assert Expression1 ;

where Expression1 is a boolean expression. When the system runs the assertion, it evaluates Expression1 and if it is false throws an AssertionError with no detail message.

The second form of the assertion statement is:

assert Expression1 : Expression2 ;

where:

Use this version of the assert statement to provide a detail message for the AssertionError. The system passes the value of Expression2 to the appropriate AssertionError constructor, which uses the string representation of the value as the error's detail message.

The purpose of the detail message is to capture and communicate the details of the assertion failure. The message should allow you to diagnose and ultimately fix the error that led the assertion to fail. Note that the detail message is not a user-level error message, so it is generally unnecessary to make these messages understandable in isolation, or to internationalize them. The detail message is meant to be interpreted in the context of a full stack trace, in conjunction with the source code containing the failed assertion.

Like all uncaught exceptions, assertion failures are generally labeled in the stack trace with the file and line number from which they were thrown. The second form of the assertion statement should be used in preference to the first only when the program has some additional information that might help diagnose the failure. For example, if Expression1 involves the relationship between two variables x and y, the second form should be used. Under these circumstances, a reasonable candidate for Expression2 would be "x: " + x + ", y: " + y.

In some cases Expression1 may be expensive to evaluate. For example, suppose you write a method to find the minimum element in an unsorted list, and you add an assertion to verify that the selected element is indeed the minimum. The work done by the assert will be at least as expensive as the work done by the method itself. To ensure that assertions are not a performance liability in deployed applications, assertions can be enabled or disabled when the program is started, and are disabled by default. Disabling assertions eliminates their performance penalty entirely. Once disabled, they are essentially equivalent to empty statements in semantics and performance. See Enabling and Disabling Assertions for more information.

The addition of the assert keyword to the Java programming language has implications for existing code. See Compatibility With Existing Programs for more information.

Putting Assertions Into Your Code

There are many situations where it is good to use assertions, including:

There are also situations where you should not use them:

Internal Invariants

Before assertions were available, many programmers used comments to indicate their assumptions concerning a program's behavior. For example, you might have written something like this to explain your assumption about an else clause in a multiway if-statement:

if (i % 3 == 0) {
    ...
} else if (i % 3 == 1) {
    ...
} else { // We know (i % 3 == 2)
    ...
}

You should now use an assertion whenever you would have written a comment that asserts an invariant. For example, you should rewrite the previous if-statement like this:

if (i % 3 == 0) {
   ...
} else if (i % 3 == 1) {
    ...
} else {
    assert i % 3 == 2 : i;
    ...
}

Note, incidentally, that the assertion in the above example may fail if i is negative, as the % operator is not a true modulus operator, but computes the remainder, which may be negative.

Another good candidate for an assertion is a switch statement with no default case. The absence of a default case typically indicates that a programmer believes that one of the cases will always be executed. The assumption that a particular variable will have one of a small number of values is an invariant that should be checked with an assertion. For example, suppose the following switch statement appears in a program that handles playing cards:

switch(suit) {
  case Suit.CLUBS:
    ...
  break;

  case Suit.DIAMONDS:
    ...
  break;

  case Suit.HEARTS:
    ...
    break;

  case Suit.SPADES:
      ...
}

It probably indicates an assumption that the suit variable will have one of only four values. To test this assumption, you should add the following default case:

default:
    assert false : suit;

If the suit variable takes on another value and assertions are enabled, the assert will fail and an AssertionError will be thrown.

An acceptable alternative is:

default:
    throw new AssertionError(suit);

This alternative offers protection even if assertions are disabled, but the extra protection adds no cost: the throw statement won't execute unless the program has failed. Moreover, the alternative is legal under some circumstances where the assert statement is not. If the enclosing method returns a value, each case in the switch statement contains a return statement, and no return statement follows the switch statement, then it would cause a syntax error to add a default case with an assertion. (The method would return without a value if no case matched and assertions were disabled.)

Control-Flow Invariants

The previous example not only tests an invariant, it also checks an assumption about the application's flow of control. The author of the original switch statement probably assumed not only that the suit variable would always have one of four values, but also that one of the four cases would always be executed. It points out another general area where you should use assertions: place an assertion at any location you assume will not be reached. The assertions statement to use is:

assert false;

For example, suppose you have a method that looks like this:

void foo() {
    for (...) {
      if (...)
        return;
    }
    // Execution should never reach this point!!!
}

Replace the final comment so that the code now reads:

void foo() {
    for (...) {
      if (...)
        return;
    }
    assert false; // Execution should never reach this point!
}

Note: Use this technique with discretion. If a statement is unreachable as defined in the Java Language Specification ( JLS 14.21), you will get a compile time error if you try to assert that it is not reached. Again, an acceptable alternative is simply to throw an AssertionError.

Preconditions, Postconditions, and Class Invariants

While the assert construct is not a full-blown design-by-contract facility, it can help support an informal design-by-contract style of programming. This section shows you how to use asserts for:

Preconditions

By convention, preconditions on public methods are enforced by explicit checks that throw particular, specified exceptions. For example:

/**
  * Sets the refresh rate.
  *
  * @param  rate refresh rate, in frames per second.
  * @throws IllegalArgumentException if rate <= 0 or
  * rate > MAX_REFRESH_RATE.
*/
public void setRefreshRate(int rate) {
  // Enforce specified precondition in public method
  if (rate <= 0 || rate > MAX_REFRESH_RATE)
    throw new IllegalArgumentException("Illegal rate: " + rate);
    setRefreshInterval(1000/rate);
  }

This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError.

You can, however, use an assertion to test a nonpublic method's precondition that you believe will be true no matter what a client does with the class. For example, an assertion is appropriate in the following "helper method" that is invoked by the previous method:

/**
 * Sets the refresh interval (which must correspond to a legal frame rate).
 *
 * @param  interval refresh interval in milliseconds.
*/
 private void setRefreshInterval(int interval) {
  // Confirm adherence to precondition in nonpublic method
  assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;

  ... // Set the refresh interval
 } 

Note, the above assertion will fail if MAX_REFRESH_RATE is greater than 1000 and the client selects a refresh rate greater than 1000. This would, in fact, indicate a bug in the library!

Lock-Status Preconditions

Classes designed for multithreaded use often have non-public methods with preconditions relating to whether or not some lock is held. For example, it is not uncommon to see something like this:

private Object[] a;
public synchronized int find(Object key) {
  return find(key, a, 0, a.length);
}

// Recursive helper method - always called with a lock on this object
private int find(Object key, Object[] arr, int start, int len) {
 ...
} 

A static method called holdsLock has been added to the Thread class to test whether the current thread holds the lock on a specified object. This method can be used in combination with an assert statement to supplement a comment describing a lock-status precondition, as shown in the following example:

// Recursive helper method - always called with a lock on this.
private int find(Object key, Object[] arr, int start, int len) {
  assert Thread.holdsLock(this); // lock-status assertion 
  ...
} 

Note that it is also possible to write a lock-status assertion asserting that a given lock isn't held.

Postconditions

You can test postcondition with assertions in both public and nonpublic methods. For example, the following public method uses an assert statement to check a post condition:

 /**
  * Returns a BigInteger whose value is (this-1 mod m).
  *
  * @param  m the modulus.
  * @return this-1 mod m.
  * @throws ArithmeticException  m <= 0, or this BigInteger
  *has no multiplicative inverse mod m (that is, this BigInteger
  *is not relatively prime to m).
  */
public BigInteger modInverse(BigInteger m) {
  if (m.signum <= 0)
    throw new ArithmeticException("Modulus not positive: " + m);
  ... // Do the computation
  assert this.multiply(result).mod(m).equals(ONE) : this;
  return result;
}

Occasionally it is necessary to save some data prior to performing a computation in order to check a postcondition. You can do this with two assert statements and a simple inner class that saves the state of one or more variables so they can be checked (or rechecked) after the computation. For example, suppose you have a piece of code that looks like this:

 void foo(int[] array) {
  // Manipulate array
  ...

  // At this point, array will contain exactly the ints that it did
  // prior to manipulation, in the same order.
 }

Here is how you could modify the above method to turn the textual assertion of a postcondition into a functional one:

 void foo(final int[] array) {

  // Inner class that saves state and performs final consistency check
  class DataCopy {
private int[] arrayCopy;

DataCopy() { arrayCopy = (int[]) array.clone(); }

boolean isConsistent() { return Arrays.equals(array, arrayCopy); }
  }

  DataCopy copy = null;

  // Always succeeds; has side effect of saving a copy of array
  assert ((copy = new DataCopy()) != null);

  ... // Manipulate array

  // Ensure array has same ints in same order as before manipulation.
  assert copy.isConsistent();
  } 

You can easily generalize this idiom to save more than one data field, and to test arbitrarily complex assertions concerning pre-computation and post-computation values.

You might be tempted to replace the first assert statement (which is executed solely for its side-effect) by the following, more expressive statement:

 copy = new DataCopy(); 

Don't make this replacement. The statement above would copy the array whether or not asserts were enabled, violating the principle that assertions should have no cost when disabled.

Class Invariants

A class invariant is a type of internal invariant that applies to every instance of a class at all times, except when an instance is in transition from one consistent state to another. A class invariant can specify the relationships among multiple attributes, and should be true before and after any method completes. For example, suppose you implement a balanced tree data structure of some sort. A class invariant might be that the tree is balanced and properly ordered.

The assertion mechanism does not enforce any particular style for checking invariants. It is sometimes convenient, though, to combine the expressions that check required constraints into a single internal method that can be called by assertions. Continuing the balanced tree example, it might be appropriate to implement a private method that checked that the tree was indeed balanced as per the dictates of the data structure:

 // Returns true if this tree is properly balanced
 private boolean balanced() {
  ...
 }

Because this method checks a constraint that should be true before and after any method completes, each public method and constructor should contain the following line immediately prior to its return:

 assert balanced(); 

It is generally unnecessary to place similar checks at the head of each public method unless the data structure is implemented by native methods. In this case, it is possible that a memory corruption bug could corrupt a "native peer" data structure in between method invocations. A failure of the assertion at the head of such a method would indicate that such memory corruption had occurred. Similarly, it may be advisable to include class invariant checks at the heads of methods in classes whose state is modifiable by other classes. (Better yet, design classes so that their state is not directly visible to other classes!)

Advanced Uses

The following sections discuss topics that apply only to resource-constrained devices and to systems where asserts must not be disabled in the field. If you have no interest in these topics, skip to the next section, "Compiling Files that Use Assertions".

Removing all Trace of Assertions from Class Files

Programmers developing applications for resource-constrained devices may wish to strip assertions out of class files entirely. While this makes it impossible to enable assertions in the field, it also reduces class file size, possibly leading to improved class loading performance. In the absence of a high quality JIT, it could lead to decreased footprint and improved runtime performance.

The assertion facility offers no direct support for stripping assertions out of class files. The assert statement may, however, be used in conjunction with the "conditional compilation" idiom described in JLS 14.20, enabling the compiler to eliminate all traces of these asserts from the class files that it generates:

 static final boolean asserts = ... ; // false to eliminate asserts

 if (asserts)
  assert <expr> ; 

Requiring that Assertions are Enabled

Programmers of certain critical systems might wish to ensure that assertions are not disabled in the field. The following static initialization idiom prevents a class from being initialized if its assertions have been disabled:

 static {
  boolean assertsEnabled = false;
  assert assertsEnabled = true; // Intentional side effect!!!
  if (!assertsEnabled)
throw new RuntimeException("Asserts must be enabled!!!");
 } 

Put this static-initializer at the top of your class.

Compiling Files That Use Assertions

In order for the javac compiler to accept code containing assertions, you must use the -source 1.4 command-line option as in this example:

 javac -source 1.4 MyClass.java 

This flag is necessary so as not to cause source compatibility problems.

Enabling and Disabling Assertions

By default, assertions are disabled at runtime. Two command-line switches allow you to selectively enable or disable assertions.

To enable assertions at various granularities, use the -enableassertions, or -ea, switch. To disable assertions at various granularities, use the -disableassertions, or -da, switch. You specify the granularity with the arguments that you provide to the switch:

For example, the following command runs a program, BatTutor, with assertions enabled in only package com.wombat.fruitbat and its subpackages:

 java -ea:com.wombat.fruitbat... BatTutor

If a single command line contains multiple instances of these switches, they are processed in order before loading any classes. For example, the following command runs the BatTutor program with assertions enabled in package com.wombat.fruitbat but disabled in class com.wombat.fruitbat.Brickbat:

 java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat BatTutor 

The above switches apply to all class loaders. With one exception, they also apply to system classes (which do not have an explicit class loader). The exception concerns the switches with no arguments, which (as indicated above) do not apply to system classes.This behavior makes it easy to enable asserts in all classes except for system classes, which is commonly desirable.

To enable assertions in all system classes, use a different switch: -enablesystemassertions, or -esa. Similarly, to disable assertions in system classes, use -disablesystemassertions, or -dsa.

For example, the following command runs the BatTutor program with assertions enabled in system classes, as well as in the com.wombat.fruitbat package and its subpackages:

 java -esa -ea:com.wombat.fruitbat... 

The assertion status of a class (enabled or disabled) is set at the time it is initialized, and does not change. There is, however, one corner case that demands special treatment. It is possible, though generally not desirable, to execute methods or constructors prior to initialization. This can happen when a class hierarchy contains a circularity in its static initialization.

If an assert statement executes before its class is initialized, the execution must behave as if assertions were enabled in the class. This topic is discussed in detail in the assertions specification.

Compatibility With Existing Programs

The addition of the assert keyword to the Java programming language does not cause any problems with preexisting binaries (.class files). If you try to compile an application that uses assert as an identifier, however, you will receive a warning or error message. In order to ease the transition from a world where assert is a legal identifier to one where it isn't, the compiler supports two modes of operation in this release:

Unless you specifically request source mode 1.4 with the -source 1.4 flag, the compiler operates in source mode 1.3. If you forget to use this flag, programs that use the new assert statement will not compile. Having the compiler use the old semantics as its default behavior (that is, allowing assert to be used as an identifier) was done for maximal source compatibility. Source mode 1.3 is likely to be phased out over time.

Design FAQ

Here is a collection of frequently asked questions concerning the design of the assertion facility.

General Questions

Compatibility

Syntax and Semantics

The AssertionError Class

Enabling and Disabling Assertions



Java Technology

Copyright © 1993, 2010, Oracle and/or its affiliates. All rights reserved.

Contact Us