.
.
java.sun.com developers.sun.com
.
   View this issue as simple text October 5, 2004    

In this Issue

Welcome to the Core Java Technologies Tech Tips for October 5, 2004. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).

This issue covers:

-Using Static Imports for Constants and Methods
-Formatting Output With the New Formatter

These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). Download JDK 5.0.

This issue of the Core Java Technologies Tech Tips is written by Daniel H. Steinberg, Director of Java Offerings for Dim Sum Thinking, Inc, and editor-in-chief for java.net.

See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.

For more Java technology content, visit these sites:

java.sun.com - The latest Java platform releases, tutorials, and newsletters.

java.net - A web forum for collaborating and building solutions together.

java.com - The marketplace for Java technology, applications and services.

.
.

USING STATIC IMPORTS FOR CONSTANTS AND METHODS

Many new language features have been introduced in the release of J2SE 5.0. Static imports is a feature that helps in the creation and use of global constants. It's one of the easiest new features to incorporate into your code. In this tip, you'll have the chance to use static imports for constants and methods. You'll start with the traditional technique of using "constant interfaces". These interfaces only exist to hold constants. These constants should be moved into utility classes, but these classes have traditionally been hard to use. The problems disappear with static imports. In addition to their use for constants, static imports can ease the calling of methods and classes. However there is a downside to using static imports in this way: it can make your code less readable. This is especially true with wild cards. It can become hard to determine where methods and attributes originate.

In the past, it was common for developers to use interfaces to store values that would be treated as global constants. Here's an example:

   public interface BadIrrationalConstants {
     public static final double SQRT_TWO = 1.414;
     public static final double SQRT_THREE = 1.732;
   }

This was particularly appealing to developers who needed more than one group of constants. For instance, a developer might need the values defined in the BadIrrationalConstants interface as well as those in the following interface, BadTranscendentalConstants:

 public interface BadTranscendentalConstants {
     public static final double PI = 3.14159;
     public static final double E = 2.71828;
   }

It is tempting to use this technique of constant interfaces because it is easy to make the global constants available for use. You simply implement each interface containing the needed constants like this:

   public class BadUseOfConstants implements
     BadTranscendentalConstants, BadIrrationalConstants {

    public static double sinPiOverFour() {
       return SQRT_TWO / 2;
     }

     public static void main(String[] args) {
       System.out.println("Pi is approximately " + PI);
       System.out.println("The sin of Pi/4 is about " +
         sinPiOverFour());
     }
   }

Before the J2SE 5.0 release, your only alternative was to use fully qualified names such as BadIrrationalConstants.SQRT_TWO everywhere. This was excessively verbose and led to hacks such as BadUseOfConstants.

The problem with the approach above is that the constants are now part of the type BadUseOfConstants. Any class that uses these constants adds them to its public interface. This is a restriction on further modifications of the class. That's because clients of the class might depend on the constants being present. Instead, the constants should be part of a class like this:

   package staticEx;

   public class IrrationalConstants {
     public static final double SQRT_TWO = 1.414;
     public static final double SQRT_THREE = 1.732;
   }

Before you are tempted to extend IrrationalConstants to use SQRT_TWO and SQRT_THREE, assume that you also need the constants defined in TranscendentalConstants:

   package staticEx;

   public class TranscendentalConstants {
     public static final double PI = 3.14159;
     public static final double E = 2.71828;
   }

Before the release of J2SE 5.0, it took quite a bit of effort to use these constants, sometimes obscuring the equations in which they were used. However, static import allows you to easily import static members into your code. For example, if you want to use the static class attribute SQRT_TWO, you declare the following:

   import static staticEx.IrrationalConstants.SQRT_TWO

at the beginning of the class in which you want to use the constant. You can then use SQRT_TWO as you did in BadTranscendentalConstants. Note that you need to use the keyword static after the keyword import. If you try the following without the static keyword, it will not compile. Compile and run the following class, ConstantsWithStaticImport, using a J2SE 5.0 compiler:

   package staticEx;

   import static staticEx.IrrationalConstants.SQRT_TWO;
   import static staticEx.IrrationalConstants.SQRT_THREE;
   import static staticEx.TranscendentalConstants.PI;

   public class ConstantsWithStaticImport {

     public static double sinPiOverFour() {
       return SQRT_TWO / 2;
     }

     public static void main(String[] args) {
       System.out.println("Pi is approximately " + PI);
       System.out.println("The sin of Pi/4 is about " +
         sinPiOverFour());
     }
   }

You should see the following displayed:

   Pi is approximately 3.14159
   The sin of Pi/4 is about 0.707

You can use wildcards in the import as you might expect. In this case, you can replace the first two import statements with:

   import static staticEx.IrrationalConstants.*;

As always, being specific and avoiding the use of the wildcard helps control exactly what is being imported. It also makes things clearer for someone reading your source code.

You can use static imports for static methods as well as for static attributes. In the class below, IrrationalConstants2, you have both:

   package staticEx;

   public class IrrationalConstants2 {
     public static final double SQRT_TWO = 1.414;
     public static final double SQRT_THREE = 1.732;

     public static double sinPiOverFour() {
       return SQRT_TWO / 2;
     }
   }

Make them all available by using the wildcard. Now you can call the static method sinPiOverFour(), which is defined in IrrationalConstants2, like this:

   package staticEx;

   import static staticEx.IrrationalConstants2.*;
   import static staticEx.TranscendentalConstants.*;

   public class ConstantsWithStaticImport2 {

     public static void main(String[] args) {
       System.out.println("Pi is approximately " + PI);
       System.out.println("The sin of Pi/4 is about " +
         sinPiOverFour());
     }
   }

You can see however that there are already readability problems. It is not easy to determine whether sinPiOverFour() is in the IrrationalConstants2 class or in TranscendentalConstants. You can clarify this by replacing the wildcards in the import with explicit references to the method being called. There is less confusion when you use a class in the standard J2SE distribution such as java.lang.Math. By using the static import, you reduce client code such as Math.PI and Math.sin() to PI and sin().

   package staticEx;

   import static java.lang.Math.*;

   public class ConstantsWithStaticImport3 {

     public static void main(String[] args) {
       System.out.println("Pi is approximately " + PI);
       System.out.println("The sin of Pi/4 is about " +
         sin(PI / 4));
     }
   }

In addition to static methods and attributes, you can use static import for static classes. For example, the class java.awt.geom.Point2D.Double is a static subclass of java.awt.geom.Point2D. If you import java.awt.geom.Point2D.Double, it can refer to all of its static members by using Double.xxx. The following example, StaticClassEx, uses the static field out in the final class, java.lang.System, to simplify calls to System.out.println(). StaticClassEx prints to standard out using out.println(), and calls the static method distance() using Double.distance().

   package staticEx;

   import static java.awt.geom.Point2D.Double;
   import static java.lang.System.out;

   public class StaticClassEx {

     public static void main(String[] args) {
       out.println("The diagonal of a unit square is " +
         Double.distance(0.0, 0.0, 1.0, 1.0) + ".");
     }
   }

The result is:

   The diagonal of a unit square is 1.4142135623730951.

When used judiciously, static imports can make your code simpler to read and make it easier to work with static variables, methods, and classes. Be careful not to overuse this technique. Also be sure to explicitly import a variable or method when there is a possibility of ambiguity.

For additional information about static imports, see the section Static Import in the description of new features and enhancements for J2SE 5.0.

.
.

FORMATTING OUTPUT WITH THE NEW FORMATTER

J2SE 5.0 introduces a new way to format output that is similar to that of the C language's printf. In this approach, each argument to be formatted is described using a string that begins with % and ends with the formatted object's type. This tip introduces you to the new Formatter class and to the syntax of the formatting that you perform with the class.

It's likely that you will most often use the new formatting approach in a call similar to either of the following:

   System.out.format("Pi is approximately  %f", Math.Pi);
   System.out.printf("Pi is approximately  %f", Math.Pi);

The printf() and the format() methods perform the same function. System.out is an instance of java.io.PrintStream. PrintStream, java.io.PrintWriter, and java.lang.String each have four new public methods:

format( String format, Object... args);
printf( String format, Object... args);
format( Locale locale, String format, Object... args);
printf( Locale locale, String format, Object... args);

These correspond to the format() method in the underlying worker class java.util.Formatter class:

   format(String format, Object... args)
   format(Locale l, String format, Object... args)

Although you'll likely use these methods from String, PrintStream, and PrintWriter, you'll find the documentation for the various available formatting options in the documentation for the Formatter class.

Let's begin with the following example of the format() method:

   formatter.format("Pi is approximately %1$f," +
        "and e is about %2$f", Math.PI, Math.E); 

The format() method requires some of the new language features introduced in J2SE 5.0. One is varargs, which simplifies the way that an arbitray number of arguments can be passed to a method. Notice that a variable number of Object instances can be entered for formatting. Also notice that the objects in the example are autoboxed and unboxed. Autoboxing/Unboxing is also a new feature in J2SE 5.0. Autoboxing eliminates the need for manually converting a primitive type (such as int) to a wrapper class (such as Integer), unboxing automates the reverse process. In the example, Math.PI is a double which is autoboxed to a Double (to be treated as an Object). In addition, in the example the formatted output is written to java.lang.StringBuilder, yet another new feature introduced in J2SE 5.0.

The format itself is a String that includes zero or more formatting elements, each beginning with a %. Each formatting element is applied to one of the Objects passed in. Each formatting element has the general form:

   %[argument_index$][flags][width][.precision]conversion

The argument index is a positive integer that indicates the position of the argument in the argument list. The numbering begins with 1 for the first position, not with 0. So the first position in the previous code snippet is occupied by Math.PI, and is indicated by using 1$. The second position is occupied by Math.E, and is indicated by using 2$.

The width specifies the minimum number of characters to be written as output.

The precision is used to restrict the number of non-zero characters.

The conversion describes the type of the object being formatted. Much of this should be familiar to C programmers because this is a Java implementation of printf(). Common types include f for float, t for time, d for decimal, o for octal, x for hexadecimal, s for general, and c for a Unicode character. The following sample application, UsingFormatter, allows you to enter different formats from the command line and view the output. Notice that the application instantiates a destination -- in this example, a StringBuilder. It then instantiates a Formatter and associates it with the destination. The Formatter then formats some String and sends the output to the destination. The results of the conversion are then displayed to standard out.

   package format;

   import java.util.Formatter;

   public class UsingFormatter {

     public static void main(String[] args) {
       if (args.length != 1) {
         System.err.println("usage: " +
           "java format/UsingFormatter ");
         System.exit(0);
       }
       String format = args[0];

       StringBuilder stringBuilder = new StringBuilder();
       Formatter formatter = new Formatter(stringBuilder);
       formatter.format("Pi is approximately " + format +
         ", and e is about " + format, Math.PI, Math.E);
       System.out.println(stringBuilder);
     }
   }

Compile and run this with the command line argument %f:

   java format/UsingFormatter %f

You should get the result:

   Pi is approximately 3.141593, and e is about 2.718282

Rerun this and set the precision to be two decimal places:

   java format/UsingFormatter %.2f 

You should see the following:

   Pi is approximately 3.14, and e is about 2.72

Notice that the numbers are not just truncated. The value of e is rounded off to two decimal places. You can additionally specify the width by supplying the command line argument %6.2f. This time leading spaces are inserted because you specified that the number should use six characters, even though the precision restricts it to using only three characters and a decimal place. If you enter the command:

   java format/UsingFormatter %6.2f

You should see this:

   Pi is approximately   3.14, and e is about   2.72

The position can be used to specify which argument to format. Rerun UsingFormatter with the command line argument %1$.2f. This specifies that you want to use Math.PI twice. You should see the following output:

   Pi is approximately 3.14, and e is about 3.14

You can change the Locale used to format the numbers by adding an import statement and calling a different constructor. Here's an example:

   package format;

   import java.util.Formatter;
   import java.util.Locale;

   public class UsingFormatter {

     public static void main(String[] args) {
       if (args.length != 1) {
         System.err.println("usage: " +
           "java format/UsingFormatter <format string>");
         System.exit(0);
       }
       String format = args[0];

       StringBuilder stringBuilder = new StringBuilder();
       Formatter formatter = new Formatter(stringBuilder,
                                   Locale.FRANCE);
       formatter.format("Pi is approximately " + format +
         ", and e is about " + format, Math.PI, Math.E);
       System.out.println(stringBuilder);
     }
   }

Compile and run this with the argument %.2f and you should see the decimal points changed to commas.

   Pi is approximately 3,14, and e is about 2,72

As previously mentioned, you will typically not use the Formatter class explicitly. Instead, for example, you can directly use the printf() and format() methods in the PrintStream class. The following program, UsingSystemOut, is a rewritten version of the UsingFormatter program to use standard out:

   package format;

   public class UsingSystemOut {

     public static void main(String[] args) {
       if (args.length != 1) {
         System.err.println("usage: " +
           "java format/UsingSystemOut <format string>");
         System.exit(0);
       }
       String format = args[0];

       System.out.format("Pi is approximately " + format +
         ", and e is approximately " + format, Math.PI, Math.E);
     }
   }

The behavior of UsingSystemOut is slightly different than that of UsingFormatter. The UsingFormatter program uses println(), the UsingSystemOut program does not. Because of that, if you run UsingSystemOut from the command line, you will notice that your next command prompt is on the same line as your output. You need to insert a new line. You can do this using formatted output by adding %n. Run UsingSystemOut with the command line argument %.2f%n:

   java format/UsingSystemOut %.2f%n

You will see the following result:

   Pi is approximately 3.14
   , and e is about 2.72

You can replace the last method call with printf() if you prefer. There is no difference between System.out.format() and System.out.printf().

As a final example, let's take a look at how date and time objects can be formatted. These objects have the conversion type t or T. That letter is followed by a second letter that indicates which part of the time should be displayed and how it should be displayed. For example, you can display the hour in a variety of forms using tH, tI, tk, or tl, and the minute within the hour using tM. These can also be combined with tr, which displays tH:tM. Similarly, you can display the day of the week, the name of the month, and so on, using the format conversion keys detailed in the Formatter API.

Here is an example that displays the date by formatting the current time using tr for the hour and minute, tA for the day of the week, tB for the name of the month, te for the number of the day of the month, and tY for the year. These could all be preceded by 1$ to point to the first position. Instead, the %tr points to the first position. The < in the other format strings refers back to the position formatted previously.

   package format;

   import java.util.Calendar;

   public class FormattingDates  {

     public static void main(String[] args) {
       System.out.printf("Right now it is %tr on " +
                         "%<tA, %<tB %<te, %<tY.%n",
                         Calendar.getInstance());
     }
   }

Compile and run the FormattingDates program. You will see output that looks something like this:

   Right now it is 01:55:19 PM on Wednesday, September 22, 2004.

This tip is intended to get you started using the new Formatter facility for formatting output. In some ways, the options should feel familiar to you from the old printf() days. Here as before, there are many possibilities available. You will only learn them by trying out different options and deciding which ones meet your needs.

For more information about formatting with the new formatter, see the documentation for the Formatter class.

.
.

OTHER RESOURCES

Got a question about Java technologies or tools? Then join these upcoming chats and webinars:

Chats:

  • Oct. 12 11:00 A.M. PDT/6:00 P.M. UTC
  • .
    .
    Rate and Review
    Tell us what you think of the content of this page.
    Excellent   Good   Fair   Poor  
    Comments:
    If you would like a reply to your comment, please submit your email address:
    Note: We may not respond to all submitted comments.
    .
    .

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

    Privacy Statement: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your email preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe.

    Comments? Send your feedback on the Core Java Technologies Tech Tips to: http://developers.sun.com/contact/feedback.jsp?category=newslet

    Subscribe to other Java developer Tech Tips:

    - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).
    - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).

    To subscribe to these and other Sun Developer Network publications:
    - Go to the Sun Developer Network Subscriptions page, choose the newsletters you want to subscribe to and click "Update".
    - To unsubscribe, go to the subscriptions page, uncheck the appropriate checkbox, and click "Update".


    ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at:
    http://java.sun.com/developer/JDCTechTips/index.html


    Copyright 2004 Sun Microsystems, Inc. All rights reserved.
    4150 Network Circle, Santa Clara, CA 95054 USA.


    This document is protected by copyright. For more information, see:
    http://java.sun.com/developer/copyright.html


    Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks (http://www.sun.com/suntrademarks/) of Sun Microsystems, Inc. in the United States and other countries.

    Sun Microsystems,
Inc.
    .
    .