.
.
java.sun.com developers.sun.com
.
   View this issue as simple text February 8, 2005    

In this Issue

Welcome to the Core Java Technologies Tech Tips for February 8, 2005. 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:

-Introduction to JOGL
-Changing Behavior Based on the Value of an Enumerated Type

These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp.

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 (http://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 Java technology source for developers. Get the latest Java platform releases, tutorials, newsletters and more.

java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together.

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

.
.

INTRODUCTION TO JOGL

As it's name implies, JOGL, the Java APIs for OpenGL, is a Java programming language binding for the OpenGL 3D graphics API. JOGL is designed to provide hardware-supported 3D graphics to applications written in Java technology. This tip will show you how to use JOGL to include basic two-dimensional and three-dimensional graphics in an application. JOGL is available under the Berkeley Software Distribution (BSD) license on java.net. Because JOGL is a light Java wrapper around the standard OpenGL API, the examples and text in this tip, for the most part, don't cover basic information on OpenGL.

To install JOGL, download the latest version from the jogl documents and files page. You'll find various release builds on the page. This tip was tested against the November 19, 2004 1.1b07 release build. You will need to download and install two sets of files. The article, Jumping into JOGL by Chris Adamson describes what files you need to download and where to put them on your machine. Note that the platform-specific files are in a jar file. You'll need to expand the jar file, and then put the platform-specific files in a directory where your runtime can find them.

After you install JOGL, you can use it to provide an application with hardware-accelerated 3D graphics. Let's start with an application that uses JOGL to display a red square that spins in the center of a black region. The example program, JOGLRotatingSquare, is shown later in this tip. The Constructor for JOGLRotatingSquare outlines the steps that are taken to set up the application:

   JOGLRotatingSquare() {
     GLCanvas canvas = getGLCanvas();
     canvas.addGLEventListener(new RotatingSquareListener());
     Animator anim = new Animator(canvas);
     addCanvasToFrame(canvas);
     anim.start();
    }

The example begins by obtaining an object of type GLCanvas through the getGLCanvas() method. It then creates and attaches a GLEventListener to the object. Then it creates an Animator object that does the animation for the scene, adds the GLCanvas object to the frame, and finally starts the animation.

The getGLCanvas() method starts by configuring a GLCapabilities object, an object that specifies the OpenGL capabilities that the rendering context must support. For example, here is how you would request that hardware acceleration be supported in the rendering context:

   GLCapabilities capabilities = new GLCapabilities();
   capabilities.setHardwareAccelerated(true);

Then the method passes the GLCapabilities object into a factory to obtain a GLCanvas. GLCanvas implements the GLDrawable interface, an interface that provides OpenGL rendering support. The GLCanvas object is used to render the scene:

   private GLCanvas getGLCanvas() {
     GLCapabilities capabilities = new GLCapabilities();
     return GLDrawableFactory.getFactory().
                           createGLCanvas(capabilities);
   }

The GLEventListener performs most of the work. In JOGLRotatingSquare, the listener is implemented as an inner class. The listener must implement the methods init(), display(), displayChanged(), and reshape().

The init() method is called by the drawable immediately after the OpenGL context is initialized for the first time. The init() method is where you do your basic environment set up. For example, you can set up the erasing and drawing color. In the JOGLRotatingSquare example, erasing and drawing color are both initially set to black:

    public void init(GLDrawable drawable) {
      GL gl = drawable.getGL();
      gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //erasing color
      gl.glColor3f(0.0f, 0.0f, 0.0f); // drawing color
    }

The displayChanged() method is called with the display mode or display device changes. In the JOGLRotatingSquare example, the displayChanged() method body is empty:

    public void displayChanged(GLDrawable drawable,
                               boolean modeChanged,
                               boolean deviceChanged) {
    }

The reshape() method is called during the first repaint after the component is resized. This includes the first time the component appears on the screen. In the JOGLRotatingSquare example, the reshape() method is used to set GLCanvas to fit the viewable area and to recenter the square if the Frame is resized:

   public class VarGreeter3 {
   public void reshape(GLDrawable drawable,
                        int x,
                        int y,
                        int width,
                        int height) {
      GL gl = drawable.getGL();
      gl.glViewport(0, 0, width, height);
      gl.glMatrixMode(GL.GL_PROJECTION);
      gl.glLoadIdentity();
      gl.glOrtho(-width, width, -height, height, -1, 1);
    }

The display() method is called to initiate the OpenGL rendering. It is also the method called by the Animator object to update the display.

In the JOGLRotatingSquare program, the display() method clears the screen, redraws the square, and performs a rotation equal to the current value of angle. This rotates the square quickly in a counter clockwise direction.

   public void display(GLDrawable drawable) {
      GL gl = drawable.getGL();
      gl.glClear(GL.GL_COLOR_BUFFER_BIT);
      drawRedCenteredSquare(gl);
      angle++;
      gl.glMatrixMode(GL.GL_MODELVIEW);
      gl.glLoadIdentity();
      gl.glRotatef(angle, 0, 0, 1);
    }

The parameters to the glRotatef() method are an angle and three other floats that indicate the axis on which the rotation takes place. In this example, the rotation is about the z-axis. This is equivalent to a counter-clockwise rotation in the x-y plane.

Here is the entire code listing for JOGLRotatingSquare:

   import net.java.games.jogl.GLCanvas;
   import net.java.games.jogl.GLCapabilities;
   import net.java.games.jogl.GLDrawableFactory;
   import net.java.games.jogl.Animator;
   import net.java.games.jogl.GL;
   import net.java.games.jogl.GLEventListener;
   import net.java.games.jogl.GLDrawable;
   
   import java.awt.Frame;
   import java.awt.event.WindowAdapter;
   import java.awt.event.WindowEvent;
   
   public class JOGLRotatingSquare {
   
     private static float angle = 0;
     private static final int SIZE = 160;
   
     JOGLRotatingSquare() {
       GLCanvas canvas = getGLCanvas();
       canvas.addGLEventListener(new RotatingSquareListener());
       Animator anim = new Animator(canvas);
       addCanvasToFrame(canvas, anim);
       anim.start();
     }
   
     private void addCanvasToFrame(
      GLCanvas canvas, final Animator anim) {
       Frame f = new Frame("JOGL Rotating Square");
       f.setSize(600, 400);
       f.add(canvas);
       f.setVisible(true);
       f.addWindowListener(new WindowAdapter() {
           public void windowClosing(WindowEvent e) {
             anim.stop();
             System.exit(0);
           }
         });
     }
   
     private GLCanvas getGLCanvas() {
       GLCapabilities capabilities = new GLCapabilities();
       return GLDrawableFactory.getFactory().
                             createGLCanvas(capabilities);
     }
 

     public static void main(String[] args) {
       new JOGLRotatingSquare();
     }
 
     private void drawRedCenteredSquare(GL gl) {
       gl.glColor3f(1, 0, 0);
       gl.glRecti(-SIZE / 2, -SIZE / 2, SIZE / 2, SIZE / 2);
       gl.glColor3f(0.0f, 0.0f, 0.0f);
     }
   
     class RotatingSquareListener implements GLEventListener {
   
       public void init(GLDrawable drawable) {
         GL gl = drawable.getGL();
         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //erasing color
         gl.glColor3f(0.0f, 0.0f, 0.0f); // drawing color
       }
   
       public void display(GLDrawable drawable) {
         GL gl = drawable.getGL();
         gl.glClear(GL.GL_COLOR_BUFFER_BIT);
         drawRedCenteredSquare(gl);
         angle++;
         gl.glMatrixMode(GL.GL_MODELVIEW);
         gl.glLoadIdentity();
         gl.glRotatef(angle, 0, 0, 1);
       }
   
       public void reshape(GLDrawable drawable,
                           int x,
                           int y,
                           int width,
                           int height) {
         GL gl = drawable.getGL();
         gl.glViewport(0, 0, width, height);
         gl.glMatrixMode(GL.GL_PROJECTION);
         gl.glLoadIdentity();
         gl.glOrtho(-width, width, -height, height, -1, 1);
       }
   
       public void displayChanged(GLDrawable drawable,
                                  boolean modeChanged,
                                  boolean deviceChanged) {
       }
     }
   }

Compile and run JOGLRotatingSquare. You should see a red square that spins in the center of a black region.

If you increase the size of the Frame, the square will recenter and the animation will slow. You can also slow the animation further by replacing the last line of the display() method with the line

   gl.glRotatef(angle/16, 0, 0, 1);

You can experiment with values other than 16.

The method drawRedCenteredSquare() uses one of the family of specific methods for drawing a rectangle. It first specifies that the current color is red. It then draws a rectangle centered at the origin with sides of length equal to SIZE. Finally, it resets the current color to black:

   private void drawRedCenteredSquare(GL gl) {
     gl.glColor3f(1, 0, 0);
     gl.glRecti(-SIZE / 2, -SIZE / 2, SIZE / 2, SIZE / 2);
     gl.glColor3f(0.0f, 0.0f, 0.0f);
   }

This works because the square is drawn in the x-y plane. In other words, the method ignores the third dimension. Another way to draw the rectangle is as follows:

   private static void drawRedCenteredSquare(GL gl) {
       gl.glColor3f(1, 0, 0);
       gl.glBegin(GL.GL_QUADS);
       gl.glTexCoord2f(0, 0);
       gl.glVertex3f(-SIZE / 2, SIZE / 2, 0);
       gl.glTexCoord2f(0, 1);
       gl.glVertex3f(-SIZE / 2, -SIZE / 2, 0);
       gl.glTexCoord2f(1, 1);
       gl.glVertex3f(SIZE / 2, -SIZE / 2, 0);
       gl.glTexCoord2f(1, 0);
       gl.glVertex3f(SIZE / 2, SIZE / 2, 0);
       gl.glEnd();
       gl.glColor3f(0.0f, 0.0f, 0.0f);
     }

Here the four vertices of a square are explicitly set. This approach seems a bit more verbose than the previous approach, but it will be useful in the next example (which moves from two to three dimensions). There is not much more work to be done in three dimensions than there was in two. First, you need to change the last line of the reshape() method so that the visible range in the z direction is increased to include the entire cube. If you omit this step, you will only see the horizontal slice of the cube that is within one unit of the x-y plane. Here is the revised line:

   gl.glOrtho(-width, width, -height, height, -SIZE, SIZE);

Next, change the end of the display() method to tilt the cube so that the corner is facing forward. Then rotate the cube on a diagonal. Here is the new end of the display() method:

      gl.glRotatef(-80, 1, 1, 0);
      gl.glRotatef(angle /16, 1, -1, 1);
      drawCenteredCube(gl);

Last, you will need to revise the method that draws the square as described above. The new method draws a square that would be the nearest face of the cube:

   private void drawSquareFace(GL gl) {
     gl.glBegin(GL.GL_QUADS);
     gl.glTexCoord2f(0, 0);
     gl.glVertex3f(-SIZE / 2, -SIZE / 2, SIZE / 2);
     gl.glTexCoord2f(0, 1);
     gl.glVertex3f(-SIZE / 2, SIZE / 2, SIZE / 2);
     gl.glTexCoord2f(1, 1);
     gl.glVertex3f(SIZE / 2, SIZE / 2, SIZE / 2);
     gl.glTexCoord2f(1, 0);
     gl.glVertex3f(SIZE / 2, -SIZE / 2, SIZE / 2);
     gl.glEnd();
   }

The addition to the three-dimensional version is the following method, drawCenteredCube(). It actually only draws three faces of the cube because that is all that a viewer will see in this example.

  private void drawCenteredCube(GL gl) {
    gl.glColor4f(1, 0, 0, 0);
    drawSquareFace(gl);
    gl.glColor4f(1, 1, 0, 0);
    gl.glRotatef(90, 1, 0, 0);
    drawSquareFace(gl);
    gl.glColor4f(0, 0, 1, 0);
    gl.glRotatef(90, 0, 1, 0);
    drawSquareFace(gl);
    gl.glColor3f(0.0f, 0.0f, 0.0f);
  }

First the color is set to red and a face is drawn. Then the model is rotated 90 degrees. The color is changed to yellow, and a second face is drawn. The model is then rotated 90 degrees in another direction so that the three faces meet at a single vertex. The color is changed to blue and the third face is drawn.

Here is the entire code listing:

   import net.java.games.jogl.GLCanvas;
   import net.java.games.jogl.GLCapabilities;
   import net.java.games.jogl.GLDrawableFactory;
   import net.java.games.jogl.Animator;
   import net.java.games.jogl.GL;
   import net.java.games.jogl.GLEventListener;
   import net.java.games.jogl.GLDrawable;
   
   import java.awt.Frame;
   import java.awt.event.WindowAdapter;
   import java.awt.event.WindowEvent;
   
   public class JOGLRotatingCube {
   
     private static final int SIZE = 160;
   
     private static float angle = 0;
   
     JOGLRotatingCube() {
       GLCanvas canvas = getGLCanvas();
       canvas.addGLEventListener(new RotatingCubeListener());
       Animator anim = new Animator(canvas);
       addCanvasToFrame(canvas, anim);
       anim.start();
     }
   
     private void addCanvasToFrame(
      GLCanvas canvas, final Animator anim) {
       Frame f = new Frame("JOGL Rotating Half - Cube");
       f.setSize(600, 400);
       f.add(canvas);
       f.setVisible(true);
       f.addWindowListener(new WindowAdapter() {
           public void windowClosing(WindowEvent e) {
             anim.stop();
             System.exit(0);
           }
         });
     }
   
     private GLCanvas getGLCanvas() {
       GLCapabilities capabilities = new GLCapabilities();
       return GLDrawableFactory.getFactory().
                             createGLCanvas(capabilities);
     }
   
     public static void main(String[] args) {
       new JOGLRotatingCube();
     }
   
   
     private void drawCenteredCube(GL gl) {
       gl.glColor4f(1, 0, 0, 0);
       drawSquareFace(gl);
       gl.glColor4f(1, 1, 0, 0);
       gl.glRotatef(90, 1, 0, 0);
       drawSquareFace(gl);
       gl.glColor4f(0, 0, 1, 0);
       gl.glRotatef(90, 0, 1, 0);
       drawSquareFace(gl);
       gl.glColor3f(0.0f, 0.0f, 0.0f);
     }
   
     private void drawSquareFace(GL gl) {
       gl.glBegin(GL.GL_QUADS);
       gl.glTexCoord2f(0, 0);
       gl.glVertex3f(-SIZE / 2, -SIZE / 2, SIZE / 2);
       gl.glTexCoord2f(0, 1);
       gl.glVertex3f(-SIZE / 2, SIZE / 2, SIZE / 2);
       gl.glTexCoord2f(1, 1);
       gl.glVertex3f(SIZE / 2, SIZE / 2, SIZE / 2);
       gl.glTexCoord2f(1, 0);
       gl.glVertex3f(SIZE / 2, -SIZE / 2, SIZE / 2);
       gl.glEnd();
     }
   
     class RotatingCubeListener implements GLEventListener {
   
       public void init(GLDrawable drawable) {
         GL gl = drawable.getGL();
         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //erasing color
         gl.glColor3f(0.0f, 0.0f, 0.0f); // drawing color
       }
   
       public void display(GLDrawable drawable) {
         GL gl = drawable.getGL();
         gl.glClear(GL.GL_COLOR_BUFFER_BIT);
   
         angle++;
         gl.glMatrixMode(GL.GL_MODELVIEW);
         gl.glLoadIdentity();
         gl.glRotatef(-80, 1, 1, 0);
         gl.glRotatef(angle /16, 1, -1, 1);
         drawCenteredCube(gl);
       }
   
       public void reshape(GLDrawable drawable,
                           int x,
                           int y,
                           int width,
                           int height) {
         GL gl = drawable.getGL();
         gl.glViewport(0, 0, width, height);
         gl.glMatrixMode(GL.GL_PROJECTION);
         gl.glLoadIdentity();
         gl.glOrtho(-width, width, -height, height, -SIZE, SIZE);
       }
   
       public void displayChanged(GLDrawable drawable,
                                  boolean modeChanged,
                                  boolean deviceChanged) {
       }
     }
   }

Compile and run JOGLRotatingCube. You should see a rotating multicolored cube.

In these examples you have seen how to quickly get up and running with JOGL. In the first example, you added a rectangle to a GLCanvas and animated it. In the second example, you extended this to creating three faces of a cube and rotated them about the common vertex. You can experiment with extending these examples by adding the other faces to the cube or by adding different shapes to the two dimensional example.

.
.

CHANGING BEHAVIOR BASED ON THE VALUE OF AN ENUMERATED TYPE

When you first use enumerated types, you might simply see them as an alternative to using constants. However they have other interesting uses. For example, you can change the behavior of a class based on the value of an enumerated type. This tip will show you how to implement enumeration constant-specific behavior in an elegant way.

Begin by creating a simple enum type that represents the four basic types of U.S. coins:

   public enum Coin1 {
     PENNY,
     NICKEL,
     DIME,
     QUARTER;
   }

You can now switch on the value of an enum. For example, you can create a method getValue() that accepts an object of type Coin1:

   static int getValue(Coin1 coin) {
     switch( coin ) {
       case PENNY: //do something
       // .. remaining cases
     }
   }

You can also iterate through the different values in Coin1 using the enhanced for:

    for (Coin1 coin: Coin1.values())

Let's put those two techniques together into a client for Coin1:

   public class Dispenser1 {

     static int getValue(Coin1 coin){
       switch(coin) {
         case PENNY:return 1;
         case NICKEL:return 5;
         case DIME: return 10;
         case QUARTER: return 25;
         default: return 0;
       }
     }
     public static void main(String[] args) {
       for (Coin1 coin: Coin1.values())  {
         System.out.println("A " + coin + " has value = "
         + getValue(coin) + ".");
       }
     }
   } 

In this example, the default method returns a value of 0. In production code, you might prefer to have the default case throw an exception.

The value of coin.toString() is the name of the enum instance, so the output will look like this:

 A PENNY has value = 1.
 A NICKEL has value = 5.
 A DIME has value = 10.
 A QUARTER has value = 25.

In a straightforward example like the previous one, the switch statement can easily be moved from the client class to the enumeration class, so that the switch information is part of the Coin enum's functionality. Later, you will see an example where you need the client to switch on the enumeration type and decide what to do itself. In Coin1, each type has a single behavior, so let's move the switch statement to the enum. This is shown in the enum Coin2, where the switch statement is in the value() method of the enum class.

Newcomers to enumerated types think of them as representing constants, and forget that you can add methods to the definition. Remember that enums have the flexibility of classes and so you have many choices regarding how or when to implement different functionality. The value() method belongs to the type and is available to each instance.

   public enum Coin2 {
     PENNY,
     NICKEL,
     DIME,
     QUARTER;

     int value(){
        switch(this) {
         case PENNY:return 1;
         case NICKEL:return 5;
         case DIME: return 10;
         case QUARTER: return 25;
         default: return 0;
       }
     }
   }

Now the client code can be simplified:

   public class Dispenser2 {

     public static void main(String[] args) {
        for (Coin2 coin: Coin2.values())  {
         System.out.println("A " + coin + " has value = "
         + coin.value() + ".");
       }
     }
   }

You can eliminate the switch statement entirely by specifying different behavior within constant-specific methods. Make value() abstract so that it can be called by client code. Defining the method in the enum itself guarantees that it can be called on any constant. Declaring the method abstract ensures each constant will override the implementation.

   public enum Coin3 {
     PENNY { int value(){ return 1;}},
     NICKEL { int value(){ return 5;}},
     DIME { int value() { return 10;}},
     QUARTER { int value() {return 25;}};

     abstract int value();
   }

You can call Coin3 by changing the appropriate line of Dispenser2 to:

   for (Coin3 coin: Coin3.values())

Enumerated types can also contain constructors. You can supply Coin3 with a field named coinValue that holds an int and is set by a constructor:

   private Coin3( int value) {
     coinValue = value;
   }

The private keyword is superfluous here because enum constructors are implicitly private. It is included here as a visual reminder that the constructor cannot be explicitly called (it is dropped in the code listing below).

Now the value() method just returns coinValue. The other change is that each class must set coinValue. You do this by changing the list from:

   PENNY, NICKEL, DIME, QUARTER;

to:

   PENNY(1), NICKEL(5), DIME(10), QUARTER(25);

Here is the modified version of Coin3:

   public enum Coin3 {
     PENNY(1),
     NICKEL(5),
     DIME(10),
     QUARTER(25);

     private int coinValue;

     int value() {return coinValue;}

     Coin3(int value){
       coinValue = value;
     }
   }

Let's return to the case where you might want different behavior in the client depending on the enumeration type. For example, if you had an Employee enum, you might need to vary behavior in different parts of a human resources application depending on whether the Employee type was SALARY, HOURLY, or HOURLY_PART_TIME. This difference might be applicable in the calculation of days off, bonuses, benefits, and pay. You don't want to implement a switch statement in each client that calls different methods based on the type that is called. Ideally, you would like the correct method to be called automatically based on the type.

One way to accomplish this is by implementing the Visitor pattern. A classic reference is Design Patterns, Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides. There are variants on the Visitor pattern -- only one version is discussed in this tip. The point of using it here is that if you use the Visitor pattern with enums, any enum constant-specific function (and any number of these functions) can be implemented by using a visitor.

If you have never seen the Visitor pattern before, you might find it a bit confusing. An instance of the enumerated type is created, and a method is called -- in this case, accept(). The class containing the behavior is passed in as an argument. The enum instance then calls the appropriate method in the class containing the behavior. In this way, different behavior can be injected into a client class.

The first component is the enumerated type. This time accept() is an abstract method that is implemented in each specialized enum constant. For example, in PENNY, accept() is implemented as follows:

   void accept( CoinVisitor cv) {cv.visitPenny(this);

In other words, this instance of accept calls the visitPenny() method in the CoinVisitor instance that was passed in as a parameter. Here is the revised enum class:

   public enum Coin4 {
     PENNY {
       void accept( CoinVisitor cv) {cv.visitPenny(this);}
     },
     NICKEL {
       void accept( CoinVisitor cv) {cv.visitNickel(this);}
     },
     DIME {
       void accept( CoinVisitor cv) {cv.visitDime(this);}
     },
     QUARTER {
       void accept( CoinVisitor cv) {cv.visitQuarter(this);}
     };
     abstract void accept(CoinVisitor cv);
   }

In this case, there is only a single implementation of CoinVisitor so you could create a concrete class. But the point of this example is to show how you can set up the infrastructure to vary the behavior in a client class. So you will need the following abstract class:

   public abstract class CoinVisitor {
     void visitPenny(Coin4 c){}
     void visitNickel(Coin4 c){}
     void visitDime(Coin4 c){}
     void visitQuarter(Coin4 c){}
   }

Now any time you want to specify a particular behavior that varies for each of the Coin4 types, you extend CoinVisitor. For example, CoinValueVisitor prints out the name and value of the appropriate coin.

   public class CoinValueVisitor extends CoinVisitor {
     void visitPenny(Coin4 c){
       System.out.println("A penny has value = 1.");
     }
     void visitNickel(Coin4 c){
       System.out.println("A nickel has value = 5.");
     }
     void visitDime(Coin4 c) {
       System.out.println("A dime has value = 10.");
     }
     void visitQuarter(Coin4 c) {
       System.out.println("A quarter has value = 25.");
     }
   }

The client code still iterates through the values of the enum, but this time, if coin is an instance of Coin4 and cvv is an instance of the CoinValueVisitor, you initiate the correct behavior with the following call:

   coin.accept(cvv);

If coin is an instance of PENNY, then this call has the effect of calling the accept() method in PENNY, and passing in cvv. Then the visitPenny() method is called on cvv. This results in the printing of "A penny has value = 1." to standard out. Here is the revised class.

   public class Dispenser4 {
      public static void main(String[] args) {
        CoinValueVisitor cvv = new CoinValueVisitor();
       for (Coin4 coin: Coin4.values())  {
         coin.accept(cvv);
       }
     }
   }

The result is the following output:

   A penny has value = 1.
   A nickel has value = 5.
   A dime has value = 10.
   A quarter has value = 25.

For more examples of changing behavior based on the value of an enumerated type, see "Enums".

.
.
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 "Submit".
- To unsubscribe, go to the subscriptions page, uncheck the appropriate checkbox, and click "Submit".


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


Copyright 2005 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.
.
.