Return-Path: <env_13489390542828235@hermes.sun.com>
Received: from pacific-carrier-annex.mit.edu by po10.mit.edu (8.9.2/4.7) id SAA07443; Tue, 9 Apr 2002 18:46:22 -0400 (EDT)
Received: from hermes.sun.com (hermes.sun.com [64.124.140.169])
	by pacific-carrier-annex.mit.edu (8.9.2/8.9.2) with SMTP id SAA07371
	for <alexp@mit.edu>; Tue, 9 Apr 2002 18:46:22 -0400 (EDT)
Date: Tue, 9 Apr 2002 15:46:22 PDT
From: "JDC Tech Tips" <body_13489390542828235@hermes.sun.com>
To: alexp@mit.edu
Message-Id: <13489390542828235@hermes.sun.com>
Subject: JDC Tech Tips, April 9, 2002 (Assertions, Currencies) 
Precedence: junk
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-Mailer: Beyond Email 


 J  D  C    T  E  C  H    T  I  P  S

                      TIPS, TECHNIQUES, AND SAMPLE CODE


WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, 
April 9, 2002. This issue covers:

         * Using Assertions
         * Representing Currencies
                 
These tips were developed using Java(tm) 2 SDK, Standard Edition, 
v 1.4.

You can view this issue of the Tech Tips on the Web at
http://java.sun.com/jdc/JDCTechTips/2002/tt0409.html

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
USING ASSERTIONS

Assertions are a new feature in Java(tm) 2 Platform, Standard 
Edition (J2SE(tm)) version 1.4. An assertion is a program
statement containing a boolean expression, that a programmer
believes to be true at the time the statement is executed. 
Assertions are used during development as a sort of internal 
sanity check, and typically are disabled when an application is 
deployed. The Java implementation of assertions involves both 
a language change (the assert keyword) and support in the library
(java.lang.AssertionError).

This tip looks at some of the details around using assertions. 
It also discusses where you might want to employ assertions in 
your Java programs. Let's start with a simple example:

    public class AssertDemo1 {
        static void f() {
            assert false;
        }
    
        public static void main(String args[]) {
            try {
                f();
            }
            catch (AssertionError e) {
                System.out.println("exception: " + e);
            }
        }
    }

If you compile and run this program by saying:

    javac -source 1.4 AssertDemo1.java

    java -ea AssertDemo1

the result is:

    exception: java.lang.AssertionError

The -source option is new for J2SE v 1.4. It's a special 
compilation switch that is required because assert is now 
a keyword rather than an identifier. Programs that use assert as
an identifier will break if assert is assumed to be a keyword. 
The switch is used as a transition mechanism.

The -ea switch is used to enable assertions at application run 
time. Without this switch, assert statements are ignored, but are 
still present in the bytecodes generated by the compiler.

If assertions are enabled, and the Java interpreter encounters
a statement of the form:

    assert boolean-expression;

it evaluates the expression. If the expression is false, the Java
interpreter throws an AssertionError exception. Because 
assertions can be disabled, the expression that's evaluated 
should be free of side effects, that is, it should not change any 
state that is visible after evaluation is complete.

This example demonstrates catching an AssertionError exception. 
An assertion failure typically represents a serious problem, not 
one that is necessarily recoverable. The fact that AssertionError 
is a subclass of Error reinforces this idea.

How can you tell from within a program whether assertions are
enabled? Here's a simple way to do that:

    public class AssertDemo2 {
        public static void main(String args[]) {
            boolean enabled = false;
    
            assert enabled = true;
    
            System.out.println("Assertions are " +
                (enabled ? "enabled" : "disabled"));
        }
    }

If you compile this program:

    javac -source 1.4 AssertDemo2.java

and then say:

    java -ea AssertDemo2

the result is:

    Assertions are enabled

If you say:

    java AssertDemo2

the result is:

    Assertions are disabled

The following line in the program:

    assert enabled = true;

is executed only if assertions are enabled, and has the effect of
setting enabled to true. If assertions are not enabled, the 
enabled variable keeps its false value.

Let's look at another example:

    class LocalClass {
        static void f(int x) {
            assert x < 0 : "x is not < 0";
        }
    }
    
    public class AssertDemo3 {
        public static void main(String args[]) {
            assert false;
            LocalClass.f(59);
        }
    }

If you run this program by saying:

    javac -source 1.4 AssertDemo3.java

    java -ea:LocalClass AssertDemo3

the result is:

    java.lang.AssertionError: x is not < 0
        at LocalClass.f(AssertDemo3.java:3)
        at AssertDemo3.main(AssertDemo3.java:10)

It is possible to selectively enable assertions for specific 
classes or packages, using the special form of the -ea switch. In 
the AssertDemo3 example, the assertion at the beginning of the 
main method is ignored. Note also that there are new features in
java.lang.ClassLoader for manipulating assertion status.

Previous examples illustrated assert statements that look like 
this:

    assert boolean-expression;

Notice that the AssertDemo3 example illustrates an alternative 
form of the statement:

    assert boolean-expression : expression;

The boolean expression is evaluated in either form. If the 
expression is false, the Java interpreter throws an 
AssertionError exception. In the:

   assert boolean-expression; 
   
case, the interpreter invokes the default (parameterless) 
AssertionError constructor. In the:

   assert boolean-expression : expression;
   
case, the second expression is evaluated, and the appropriate 
AssertionError constructor is invoked. The AssertionError 
constructor is overloaded to accept an argument of any primitive 
or object type.

What about performance? What is the cost of assertions? If
assertions are disabled, then the cost is what is required to 
check a global state flag. This is a flag that indicates 
whether assertions are in force. If you want to see what this 
check looks like in the bytecodes, you can say:

    javap -c -classpath . LocalClass

for the last example above. Here are the bytecodes for the f
method:

    Method void f(int)
       0 getstatic #7 <Field boolean $assertionsDisabled>
       3 ifne 20
       6 iload_0
       7 iflt 20
      10 new #8 <Class java.lang.AssertionError>
      13 dup
      14 ldc #9 <String "x is not < 0">
      16 invokespecial #10 <Method
         java.lang.AssertionError(java.lang.Object)>
      19 athrow
      20 return

The first line of the bytecode is used to evaluate the assertion
status. If assertions are disabled, the method returns. 
Otherwise, the assertion expression is evaluated. If the 
expression is false, an AssertionError is thrown.

If assertions are enabled, the cost is what is required to check 
the status, evaluate the boolean expression, and throw the 
exception.

What about code size? What if assertions are disabled, and you 
want to totally remove the assertion checking logic from your 
code? There is no direct mechanism for doing this, but you can 
use what is called the "conditional compilation idiom". Here's 
an example:

    class Globals {
        private Globals() {}
    
        public static final boolean DEBUG = true;
    }
    
    public class AssertDemo4 {
        public static void main(String args[]) {
            int x = 59;
            if (Globals.DEBUG) {
                assert x < 0 : "x is not < 0";
            }
        }
    }

If you change DEBUG in this example to false, and then say:

    javac -source 1.4 AssertDemo4.java

    javap -c -classpath . AssertDemo4

the bytecodes for the main method are:

    Method void main(java.lang.String[])
       0 bipush 59
       2 istore_1
       3 return

There's no trace of assertion code. Note that this technique 
depends on a Java compiler optimizing away the contents of a 
never-executed block:

    if (false) {
        // block contents
    }

Where are assertions useful? Let's consider a couple of examples. 
The first one implements a method that computes the number of
minutes between two calendar dates:

    import java.text.*;
    import java.util.*;
    
    public class AssertDemo5 {
   
        /**
         * Computes the number of minutes between 
         * two Dates.
         *
         * @param  d1  the first Date
         * @param  d2  the second Date
         *
         * @return the absolute value of the number 
         * of minutes
         *
         * @exception  NullPointerException if either 
         * Date null
         */
        public static long getDateDiff(
                                    Date d1, Date d2) {
    
            //assert d1 != null && d2 != null;
    
            if (d1 == null || d2 == null) {
                throw new NullPointerException(
                                      "d1 or d2 null");
            }
    
            // get the difference in milliseconds and
            // take the absolute value
    
            long diff = d1.getTime() - d2.getTime();
            //diff = (diff < 0 ? -diff : diff);
    
            // convert milliseconds to seconds 
            // and then to minutes
    
            long res = diff / (1000 * 60);
    
            // sanity check on the result 
            // it should be >= 0
    
            assert res >= 0 : 
                         "date difference is negative";
    
            return res;
        }
  
        public static void main(String args[])
        throws ParseException {
    
            // set up a DateFormat for parsing dates
    
            DateFormat df = 
                          DateFormat.getDateInstance();
    
            // set up a couple of dates 
            // and compute difference
    
            Date d1 = df.parse("February 12, 2002");
            Date d2 = df.parse("March 12, 2002");
            long diff = getDateDiff(d1, d2);
    
            System.out.println(
                  "difference = " + diff + " minutes");
        }
    }

Compile this program and run it with assertions enabled:

    javac -source 1.4 AssertDemo5.java

    java -ea AssertDemo5
 
the result is:

    java.lang.AssertionError: 
        date difference is negative
        at AssertDemo5.getDateDiff(AssertDemo5.java:29)
        at AssertDemo5.main(AssertDemo5.java:44)
   
The getDateDiff method computes the number of minutes between 
dates, and returns the absolute value of the result. 
Unfortunately, the programmer failed to take the absolute value 
after computing the number of minutes. But the programmer was 
smart enough to add an assertion to the code. The assertion 
captures the requirement that the result of the calculation is 
a zero or positive value.

You can fix the programmer's mistake by uncommenting the 
following line, which takes the absolute value of the diff 
variable:

    //diff = (diff < 0 ? -diff : diff);
    
If you recompile the program and run it with assertions enabled,
you get:    

    difference = 40320 minutes

There's another issue with this example. Why not use assertions 
to check the passed-in parameter values d1/d2 for the getDateDiff
method? The d1/d2 references might be null. There's some commented
code showing how such assertion checking could be done:

    assert d1 != null && d2 != null;

In this case, however, using an assertion isn't appropriate. The 
reason is that checking non-null object references is part of the 
public contract of the getDateDiff method. In other words, a check 
is made at the top of the method to see if d1 or d2 are null. If 
this logic is replaced by assertions, and assertion checking is 
disabled, then the public contract will not be enforced.

Even without the null reference checking code, there's an 
implicit contract within getDateDiff, in that object accesses 
through a null reference are guaranteed to throw a 
NullPointerException. Using assertions in this situation is not
dependable, and even if it was, the resulting exception would not 
be a NullPointerException. Instead, it would be an AssertionError.

Here's a final example that illustrates another situation where 
assertions are useful:

    import java.util.Random;
    
    public class AssertDemo6 {
    
        static Random rn = new Random(0);
    
        // original method, using random numbers
        // and a switch statement
    
        static void f1() {
            switch (rn.nextInt(3)) {
                case 0:
                    System.out.println("0");
                    break;
                case 1:
                    System.out.println("1");
                    break;
                case 2:
                    System.out.println("2");
                    break;
            }
        }
   
        // method changed to use a wider range of
        // random numbers, but switch statement 
        // not updated
    
        static void f2() {
            switch (rn.nextInt(4)) {
                case 0:
                    System.out.println("0");
                    break;
                case 1:
                    System.out.println("1");
                    break;
                case 2:
                    System.out.println("2");
                    break;
                //default:
                //  assert false;
            }
        }
    
        public static void main(String args[]) {
            for (int i = 1; i <= 10; i++) {
                f1();
            }
    
            System.out.println("======");
    
            for (int i = 1; i <= 10; i++) {
                f2();
            }
        }
    }

The AssertDemo6 code uses random numbers as part of a simulation 
scheme. The f1 method generates a random number between 0 and 2,
and uses it to dispatch within a switch statement. Each case in 
the switch is called approximately one third of the time. The f2 
method is similar to f1, but the range of random numbers is 
widened to 0-3. This means that each case should be called one
fourth of the time.

When you compile and run this program, the result is:

    0
    1
    1
    2
    2
    2
    2
    0
    0
    2
    ======
    1
    1
    1
    2
    0
    0

Ten values are printed from f1, but only six from f2, even though 
f2 is called ten times. The problem is that the f2 method was not
completely updated when the random number range was widened from 
0-2 to 0-3. There's a missing case in the switch statement. 
Because there's no default case specified, the situation where 
the random number is 3 is silently ignored.

The solution to this problem is of course to add the missing 
case. But it's also important to add a default case with an 
assertion to catch situations like this in the future.

Other situations where you might want to use assertions include 
enforcing class invariants, or checking the arguments at the top 
of a private method.

For more information about assertions, see Programming With 
Assertions 
(http://java.sun.com/j2se/1.4/docs/guide/lang/assert.html). Also 
see section 14.20 Unreachable Statements, in "The Java Language 
Specification Second Edition" 
(http://java.sun.com/docs/books/jls/).

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
REPRESENTING CURRENCIES

java.util.Currency is a new class in J2SE v 1.4. It is used to 
represent currencies, for example US dollars or Japanese yen. 
Objects of the java.util.Currency class do not represent a 
particular amount of currency, but the currency unit itself.

Let's look at a simple example of how you use the Currency class:

    import java.util.*;
    
    public class CurrDemo1 {
        public static void main(String args[]) {
    
            // get Currency instance for US
            // locale and display symbol
   
            Currency c1 = Currency.getInstance(
                                            Locale.US);
            System.out.println("US Dollar symbol = " +
                c1.getSymbol());
    
            // get symbol for US Dollar 
            // when used in Canada
    
            System.out.println(
                     "US Dollar symbol in Canada = " +
                         c1.getSymbol(Locale.CANADA));
    
            // get Currency instance for Japan 
            // based on locale
   
            Currency c2 = Currency.getInstance(
                                         Locale.JAPAN);
            System.out.println(
                         "Currency code for Japan = " +
                                 c2.getCurrencyCode());
    
            // get Currency instance for Japan 
            // based on code
    
            Currency c3 = Currency.getInstance("JPY");

            // compare currency objects
 
            if (c2 != c3) {
                System.out.println(
                                "objects are unequal");
            }
        }
    }

You designate a specific currency based on its ISO 4217 currency
code, for example "USD" or "JPY". Or you can specify a currency
based on a locale, for example "Locale.JAPAN". A list of currency
codes can be found at the BSI Currency Code Service (ISO 4217 
Maintenance Agency) site (http://www.bsi-global.com/
Import+Export+Advice/Useful+Publications/Technical/TH42090.xalter).

The Currency.getInstance method returns a Currency object that is
instance-controlled. This means that there is only one instance 
for each currency. So Currency objects can be compared using the 
== operator (reference identity).

The Currency.getSymbol method returns the symbol for a currency, 
for example "$" for the US dollar. If a symbol cannot be 
determined, then the currency code is returned. The symbol for 
a specific currency can vary based on the locale specified to
getSymbol. For example, in the United States, the US dollar 
symbol is "$", but in Canada, the symbol is "USD".

Compile and run the CurrDemo1 program. The result is:

    US Dollar symbol = $
    US Dollar symbol in Canada = USD
    Currency code for Japan = JPY

Here's another example. This program uses the Currency class to 
perform financial calculations:

    import java.math.*;
    import java.util.*;
   
    public class CurrDemo2 {
        static void calculate(
             Currency curr, String num, String denom) {
    
            // set up BigDecimal values for
            // numerator and denominator
    
            BigDecimal d1 = new BigDecimal(num);
            BigDecimal d2 = new BigDecimal(denom);
    
            // get fraction digits and divide
    
            int fracdig = 
                       curr.getDefaultFractionDigits();
            BigDecimal d3 = d1.divide(d2, fracdig,
                BigDecimal.ROUND_DOWN);
    
            // display result
    
            System.out.println(
                        curr.getSymbol() + d1 + " / " +
                   d2 + " = " + curr.getSymbol() + d3);
        }
    
        public static void main(String args[]) {
            Currency curr;
    
            String num = "147";
            String denom = "12";
    
            // do calculation for US locale
 
            curr = Currency.getInstance(Locale.US);
            calculate(curr, num, denom);
    
            // do calculation for Japanese locale
    
            curr = Currency.getInstance(Locale.JAPAN);
            calculate(curr, num, denom);
        }
    }

In the CurrDemo2 example, there are 147 units of some currency to 
be divided into 12 parts. The BigDecimal class is used for the 
calculations. The calculate method is passed a Currency object 
along with the quantities 147 and 12.

The Currency object is queried to obtain the default fraction 
digits for the currency. Then this number is used in the 
BigDecimal division to scale the result. The default digits value 
is 2 for the US dollar, and 0 for the Japanese yen.

The output of the the CurrDemo2 example is:

    $147 / 12 = $12.25
    JPY147 / 12 = JPY12

The Currency class gives you a standard way to represent 
currencies in your applications.

For more information on the Currency class, see the 
Internationalization section in the J2SE version 1.4 Summary of 
New Features and Enhancements 
(http://java.sun.com/j2se/1.4/docs/relnotes/features.html#i18n).

.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .

IMPORTANT: Please read our Terms of Use, Privacy, and Licensing 
policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/ 
http://developer.java.sun.com/berkeley_license.html

* FEEDBACK
  Comments? Send your feedback on the JDC Tech Tips to: 
  jdc-webmaster@sun.com

* SUBSCRIBE/UNSUBSCRIBE
  - To subscribe, go to the subscriptions page,
    (http://developer.java.sun.com/subscription/), choose
    the newsletters you want to subscribe to and click "Update".
  - To unsubscribe, go to the subscriptions page,
    (http://developer.java.sun.com/subscription/), uncheck the
    appropriate checkbox, and click "Update".
  - To use our one-click unsubscribe facility, see the link at 
    the end of this email:
    
- ARCHIVES
You'll find the JDC Tech Tips archives at:

http://java.sun.com/jdc/TechTips/index.html


- COPYRIGHT
Copyright 2002 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright. For more information, see:

http://java.sun.com/jdc/copyright.html

This issue of the JDC Tech Tips is written by Glen McCluskey.

JDC Tech Tips 
April 9, 2002

Sun, Sun Microsystems, Java, Java Developer Connection, and J2SE 
are trademarks or registered trademarks of Sun Microsystems, Inc. 
in the United States and other countries.

To use our one-click unsubscribe facility, select the following URL:
http://bulkmail.sun.com/unsubscribe?13489390542828235
