Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Java 101: The next generation: The essential Java language features tour, Part 1

Java programming with assertions and generics

  • Print
  • Feedback

Page 2 of 9

Exception in thread "main" java.lang.AssertionError
    at AssertDemo.main(AssertDemo.java:6)

This message is somewhat cryptic in that it doesn't identify what caused the AssertionError to be thrown. If you want a more informative message, use the assert statement expressed below:

assert BooleanExpr : expr;

Here, expr is any expression (including a method invocation) that can return a value -- you cannot invoke a method with a void return type. A useful expression is a string literal that describes the reason for failure, as demonstrated in Listing 2.

Listing 2. AssertDemo.java (version 2)

public class AssertDemo
{
   public static void main(String[] args)
   {
      int x = -1;
      assert x >= 0: "x < 0";
   }
}

Compile Listing 2 (javac AssertDemo.java) and run it with assertions enabled (java -ea AssertDemo). This time, you should observe the following slightly expanded output, which includes the reason for the thrown AssertionError:

Exception in thread "main" java.lang.AssertionError: x < 0
    at AssertDemo.main(AssertDemo.java:6)

For either example, running AssertDemo without the -ea (enable assertions) option results in no output. When assertions are not enabled, they are not executed, although they are still present in the classfile.

Testing preconditions and postconditions with assertions

Assertions are often used to test a program's preconditions and postconditions:

  • A precondition is a condition that must evaluate to true before the execution of some code sequence. Preconditions ensure that callers keep their contracts with callees.
  • A postcondition is a condition that must evaluate to true after the execution of some code sequence. Postconditions ensure that callees keep their contracts with callers.

Preconditions

You can enforce preconditions on public constructors and methods by making explicit checks and throwing exceptions when necessary. For private helper methods, you can enforce preconditions by specifying assertions. Consider Listing 3.

Listing 3. AssertDemo.java (version 3)

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

class PNG
{
   /**
    *  Create a PNG instance, read specified PNG file, and decode
    *  it into suitable structures.
    *
    *  @param filespec path and name of PNG file to read
    *
    *  @throws NullPointerException when <code>filespec</code>
is
    *          <code>null</code>
    */
   PNG(String filespec) throws IOException
   {
      // Enforce preconditions in non-private constructors and
      // methods.

      if (filespec == null)
         throw new NullPointerException("filespec is null");
      try (FileInputStream fis = new FileInputStream(filespec))
      {
         readHeader(fis);
      }
   }

   private void readHeader(InputStream is) throws IOException
   {
      // Confirm that precondition is satisfied in private
      // helper methods.

      assert is != null : "null passed to is";
   }
}

public class AssertDemo
{
   public static void main(String[] args) throws IOException
   {
      PNG png = new PNG((args.length == 0) ? null : args[0]);
   }
}

The PNG class in Listing 3 is the minimal beginning of a library for reading and decoding PNG (portable network graphics) image files. The constructor explicitly compares filespec with null, throwing NullPointerException when this parameter contains null. The point is to enforce the precondition that filespec not contain null.

It's not appropriate to specify assert filespec != null; because the precondition mentioned in the constructor's Javadoc wouldn't (technically) be honored when assertions were disabled. (In fact, it would be honored because FileInputStream() would throw NullPointerException, but you shouldn't depend on undocumented behavior.)

However, assert is appropriate in the context of the private readHeader() helper method, which will be completed eventually to read and decode a PNG file's 8-byte header. The precondition that is always be passed a nonnull value will always hold.

Postconditions

Postconditions are typically specified via assertions, regardless of whether or not the method (or constructor) is public. Consider Listing 4.

Listing 4. AssertDemo.java (version 4)

public class AssertDemo
{
   public static void main(String[] args)
   {
      int[] array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 };
      sort(array);
      for (int element: array)
         System.out.printf("%d ", element);
      System.out.println();
   }

   private static boolean isSorted(int[] x)
   {
      for (int i = 0; i < x.length-1; i++)
         if (x[i] > x[i+1])
            return false;
      return true;
   }

   private static void sort(int[] x)
   {
      int j, a;
      // For all integer values except the leftmost value ...
      for (int i = 1; i < x.length; i++)
      {
         // Get integer value a.
         a = x[i];
         // Get index of a. This is the initial insert position, which is
         // used if a is larger than all values in the sorted section.
         j = i;
         // While values exist to the left of a's insert position and the
         // value immediately to the left of that insert position is
         // numerically greater than a's value ...
         while (j > 0 && x[j-1] > a)
         {
            // Shift left value -- x[j-1] -- one position to its right --
            // x[j].
            x[j] = x[j-1];
            // Update insert position to shifted value's original position
            // (one position to the left).
            j--;
         }
         // Insert a at insert position (which is either the initial insert
         // position or the final insert position), where a is greater than
         // or equal to all values to its left.
         x[j] = a;
      }

      assert isSorted(x): "array not sorted";
   }
}

Listing 4 presents a sort() helper method that uses the insertion sort algorithm to sort an array of integer values. I've used assert to check the postcondition of x being sorted before sort() returns to its caller.

The example in Listing 4 demonstrates an important characteristic of assertions, which is that they're typically expensive to execute. For this reason, assertions are usually disabled in production code. In Listing 4, isSorted() must scan through the entire array, which can be time-consuming in the case of a lengthy array.

Generics in Java 5

In addition to the Java Concurrency Utilities (profiled in June 2013 for this series), Java 5 added eight new language features: generics, typesafe enums, annotations, autoboxing and unboxing, enhanced for loop, static imports, varargs, and covariant return types. I'll cover all of these Java 5 features over the next two articles, starting here with generics.

Generics is a suite of language features that allow types or methods to operate on objects of various types while providing compile-time type safety. Generics address the problem of java.lang.ClassCastExceptions thrown at runtime due to code that is not type safe.

  • Print
  • Feedback

Resources
  • Download the source code for this article.
  • Read Angelika Langer's Java Generics FAQs for a wealth of information and perspective about generics in the Java language.
  • For students of the Java language and its controversies, Langer's "Und erstanding the closures debate" (JavaWorld, June 2008) compares the three initial proposals for adding closures, or lambda expressions, to the Java language in Java 7.
  • See "Java Reflection: Generics" (Jakob Jenkov, Jenkov.com) for further discussion about reflection with generics and special cases where it is possible to access generics information at runtime.
  • More from Java 101: The next generation:
  • More about the Java Collections Framework on JavaWorld: