Subject: Core Java Technology Tech Tips for July 2007

From: SDN Editorial Staff <[email-masked]>
Date: Wed, 25 Jul 2007 14:13:23 -0700 (PDT)

You are receiving this email at [email-masked] because you have 
subscribed to Java Technologies Tech Tips. To update your 
communications preferences, please see the links at the bottom of 
this message. We respect your privacy and post our privacy policy 
prominently on our Web site: 
http://www.sun.com/privacy 
            
Please do not reply to the mailed version of the newsletter; this 
alias is not monitored. Feedback options are listed in the footer. 
 
Listeners vs Adapters and BigDecimal: Core Java Technologies 
Tech Tips 
 
Welcome to the Core Java Technologies Tech Tips for July 2007. Core 
Java Technologies Tech Tips provides tips and hints for using core 
Java technologies and APIs in the Java Platform, Standard Edition 6 
(Java SE 6). 
 
You can now read the Core Java Technologies Tech Tips online as a 
web log. 
http://communications1.sun.com/r/c/r?2.1.3J1.2Vc.11Hawu.C43uNM..T.Eazk.1kEg.DfCEdF00 
 
In this issue, you'll find tips for the following: 
* Listeners vs Adapters 
* The Need for BigDecimal 
 
These tips were developed using Java SE 6. You can download the Java 
Platform, Standard Edition 6 Development Kit (JDK 6), to use these 
and future tech tips, from the Java SE Downloads page by 
using the link below: 
http://communications1.sun.com/r/c/r?2.1.3J1.2Vc.11Hawu.C43uNM..T.Eazq.1kEg.EPeEdL00 
 
The author of this month's tips is John Zukowski, president and 
principal consultant of JZ Ventures, Inc. 
 
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. 
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 
LISTENERS VS. ADAPTERS 
 
The JavaBeans component model (and thus the Swing component set) is 
built upon properties and events. Properties have setter 
and getter methods for working with their values. Events require 
you to use listeners and to implement interfaces in order to 
receive notification of their occurrence. Although working with 
properties is simple, listener objects require a little extra 
discussion to understand how they work, typically in the graphical 
user interface (GUI) world. Specifically, this tip describes the AWT 
and Swing event-related classes that offer both a listener interface 
and an adapter implementation. 
 
The following classes show examples of listener and adapter pairs: 
 
package java.awt.event 
- ComputerListener/ComputerAdapter 
- ContainerListener/ContainerAdapter 
- FocusListener/FocusAdapter 
- HierarchyBoundsListener/HierarchyBoundsAdapter 
- KeyListener/KeyAdapter 
- MouseListener/MouseAdapter 
- MouseMotionListener/MouseMotionAdapter 
- WindowListener/WindowAdapter 
 
package java.awt.dnd 
- DragSourceListener/DragSourceAdapter 
- DragTargetListener/DragTargetAdapter 
 
package javax.swing.event 
- InternalFrameListener/InternalFrameAdapter 
- MouseInputListener/MouseInputAdapter 
 
These class pairs offer two ways to do the same thing. First, 
consider a simple example that doesn't offer an adapter class. The 
ActionListener class has a single actionPerformed method. Using an 
anonymous inner class, you will typically use an ActionListener 
class in the following manner: 
 
ActionListener listener = new ActionListener() { 
    public void actionPerformed(ActionEvent actionEvent) { 
      System.out.println("Event happened"); 
    } 
}; 
 
You can also use the actionPerformed method by implementing the 
ActionListener interface in a high-level class: 
 
public class MyClass extends JFrame implements ActionListener { 
    ... 
    public void actionPerformed(ActionEvent actionEvent) { 
      System.out.println("Event happened"); 
    } 
} 
 
The ActionListener interface has a single method, and implementers 
of the interface must provide an implementation of that single 
method for it to do much of anything. 
 
Other listener interfaces aren't quite so simplistic. For example, 
the MouseMotionListener interface has two methods: mouseDragged and 
mouseMoved. When you implement an interface, you must implement all 
the methods defined by the interface: 
 
MouseMotionListener listener = new MouseMotionListener() { 
    public void mouseDragged(MouseEvent mouseEvent) { 
      System.out.println("I'm dragging: " + mouseEvent); 
    } 
    public void mouseMoved(MouseEvent mouseEvent) { 
      System.out.println("I'm moving: " + mouseEvent); 
    } 
}; 
 
There are situations when your application doesn't need to track all 
events for a particular listener interface. Maybe your code only 
needs to respond to one or two of the methods in a listener 
interface. For instance, do you really want to know when a mouse 
moves, or only when it moves with a mouse button depressed? You 
cannot implement just one of the MouseMotionListener methods and 
leave the others out: 
 
MouseMotionListener badListener = new MouseMotionListener() { 
    public void mouseDragged(MouseEvent mouseEvent) { 
      System.out.println("I'm dragging: " + mouseEvent); 
    } 
}; 
 
This listener implementation will result in a compile-time error 
since an interface isn't fully implemented. With an interface 
like MouseMotionListener, that isn't too much of a problem, you 
just have to provide a stub for the method you aren't 
interested in: 
 
MouseMotionListener listener = new MouseMotionListener() { 
    public void mouseDragged(MouseEvent mouseEvent) { 
      System.out.println("I'm dragging: " + mouseEvent); 
    } 
    public void mouseMoved(MouseEvent mouseEvent) { 
      // Do nothing 
    } 
}; 
 
Not all listener interfaces are so small. Although 
MouseMotionListener has only two methods, the MouseListener 
interface has five: 
 
* void mouseClicked(MouseEvent mouseEvent) 
* void mouseEntered(MouseEvent mouseEvent) 
* void mouseExited(MouseEvent mouseEvent) 
* void mousePressed(MouseEvent mouseEvent) 
* void mouseReleased(MouseEvent mouseEvent) 
 
If you want to add a MouseListener to a component, your interface 
implementation must have five methods in it: 
 
MouseListener mouseListener = new MouseListener() { 
  public void mouseClicked(MouseEvent mouseEvent) { 
      System.out.println("I'm clicked: " + mouseEvent); 
  } 
  public void mouseEntered(MouseEvent mouseEvent) { 
      System.out.println("I'm entered: " + mouseEvent); 
  } 
  public void mouseExited(MouseEvent mouseEvent) { 
      System.out.println("I'm exited: " + mouseEvent); 
  } 
  public void mousePressed(MouseEvent mouseEvent) { 
      System.out.println("I'm pressed: " + mouseEvent); 
  } 
  public void mouseReleased(MouseEvent mouseEvent)  { 
      System.out.println("I'm released: " + mouseEvent); 
  } 
}; 
 
If your application only needs to know whether the mouse is pressed 
or released over a component, the other three methods will be empty 
and ignored. Those methods are unnecessary code. The adapter classes 
can help reduce the amount of code you must write when your 
application needs only a small subset of all interface methods. Each 
adapter class fully implements its associated interface (or 
interfaces). Then, if you want a listener for a subset of associated 
methods, you just have to provide that subset. No empty stubs 
required. Here is just such an adapter for the required 
MouseListener previously described. 
 
MouseListener mouseListener = new MouseAdapter() { 
  public void mousePressed(MouseEvent mouseEvent) { 
      System.out.println("I'm pressed: " + mouseEvent); 
  } 
  public void mouseReleased(MouseEvent mouseEvent)  { 
      System.out.println("I'm released: " + mouseEvent); 
  } 
}; 
 
This code still creates a MouseListener. However, instead of 
implementing all the interface methods that you don't care about, 
with the help of MouseAdapter, you only have to implement those 
MouseListener methods you are truly interested in. 
 
Not every multi-method listener has an adapter. You can certainly 
create your own if you constantly find yourself stubbing out most 
of an interface. Of the built-in classes, only the listeners listed 
at the top of this tip offer them. Also, the adapters are true 
classes, not interfaces. If you want your custom JButton subclass to 
also implement MouseListener, you cannot have that class subclass 
MouseAdapter, as only single inheritance is allowed. For example, 
the following code causes a compilation-time error because it 
attempts to subclass both JButton and MouseAdapter: 
 
public class BadJButtonSubclass extends JButton, MouseAdapter { 
   ... 
  public void mousePressed(MouseEvent mouseEvent) { 
      System.out.println("I'm pressed: " + mouseEvent); 
  } 
} 
 
If you truly wanted this JButton subclass to be a MouseListener, 
you must explicitly say so, AND make sure all the methods of the 
interface are implemented: 
 
public class GoodJButtonSubclass extends JButton implements 
MouseListener { 
  ... 
  public void mouseClicked(MouseEvent mouseEvent) { 
    // Do nothing 
  } 
  public void mouseEntered(MouseEvent mouseEvent) { 
    // Do nothing 
  } 
  public void mouseExited(MouseEvent mouseEvent) { 
    // Do nothing 
  } 
  public void mousePressed(MouseEvent mouseEvent) { 
      System.out.println("I'm pressed: " + mouseEvent); 
  } 
  public void mouseReleased(MouseEvent mouseEvent)  { 
    // Do nothing 
  } 
  ... 
     addMouseListener(this); 
  ... 
} 
 
Of course, you don't have to have your high-level class implement 
the interface itself. This may be a good example of when you should 
create the listener as an inner or anonymous class instead. 
 
If you use an integrated development environment (IDE) to create 
your user interface, the IDE will often generate the interface 
framework for you. You will need to code the business logic inside 
the necessary interface methods. An IDE can simplify the 
implementation of a large interface. 
 
For more information about this topic, read the How to Write a Mouse 
Listener lesson of The Java Tutorial. 
http://communications1.sun.com/r/c/r?2.1.3J1.2Vc.11Hawu.C43uNM..T.Eazs.1kEg.EVSEdN00 
 
Adapters aren't limited to mouse listening. However, the 
MouseAdapter is a frequent example because the MouseListener 
interface has so many methods. The WindowListener interface is also 
another large interface, and it has an associated WindowAdapter 
class. 
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 
THE NEED FOR BIGDECIMAL 
 
Working with floating point numbers can be fun. Typically, when 
working with amounts, you automatically think of using a double 
type, unless the value is a whole number, then an int type is 
typically sufficient. A float or long can also work out, depending 
upon the size of a value. When dealing with money, though, these 
types are absolutely the worst thing you can use as they don't 
necessarily give you the right value, only the value that can be 
stored in a binary number format. Here is a short example that shows 
the perils of using a double for calculating a total, taking into 
account a discount, and adding in sales tax. 
 
The Calc program starts with an amount of $100.05, then gives the 
user a 10% discount before adding back 5% sale tax. Your sales tax 
percentage may vary, but this example will use 5%. To see the 
results, the class uses the NumberFormat class to format the results 
for what should be displayed as currency. 
 
import java.text.NumberFormat; 
 
public class Calc { 
  public static void main(String args[]) { 
    double amount = 100.05; 
    double discount = amount * 0.10; 
    double total = amount - discount; 
    double tax = total * 0.05; 
    double taxedTotal = tax + total; 
    NumberFormat money = NumberFormat.getCurrencyInstance(); 
    System.out.println("Subtotal : "+ money.format(amount)); 
    System.out.println("Discount : " + money.format(discount)); 
    System.out.println("Total : " + money.format(total)); 
    System.out.println("Tax : " + money.format(tax)); 
    System.out.println("Tax+Total: " + money.format(taxedTotal)); 
  } 
} 
 
Using a double type for all the internal calculations produces the 
following results: 
 
Subtotal : $100.05 
Discount : $10.00 
Total : $90.04 
Tax : $4.50 
Tax+Total: $94.55 
 
The Total value in the middle is what you might expect, but that 
Tax+Total value at the end is off. That discount should be $10.01 to 
give you that $90.04 amount. Add in the proper sales tax and the 
final total goes up a penny. The tax office won't appreciate that. 
The problem is rounding error. Calculations build on those rounding 
errors. Here are the unformatted values: 
 
Subtotal : 100.05 
Discount : 10.005 
Total : 90.045 
Tax : 4.50225 
Tax+Total: 94.54725 
 
Looking at the unformatted values, the first question you might ask 
is why does 90.045 round down to 90.04 and not up to 90.05 as you 
might expect(or why does 10.005 round to 10.00)? This is 
controlled by what is called the RoundingMode, an enumeration 
introduced in Java SE 1.6 that you had no control over in prior 
releases. The acquired NumberFormat for currencies has a default 
rounding mode of HALF_EVEN. This means that when the remaining value 
is equidistant to the edges, to round towards the even side. 
According to the Java platform documentation for the enumeration, 
this will statistically minimize cumulative errors after multiple 
calculations. 
 
The other available modes in the RoundingMode enumeration are: 
 
* CEILING which always rounds towards positive infinity 
* DOWN which always rounds towards zero 
* FLOOR which always rounds towards negative infinity 
* UP which always rounds away from zero 
* HALF_DOWN which always rounds towards nearest neighbor, unless 
both neighbors are equidistant, in which case it rounds down 
* HALF_UP which always rounds towards nearest neighbor, unless both 
neighbors are equidistant, in which case it rounds up 
* UNNECESSARY which asserts exact result, with no rounding necessary 
 
Before looking into how to correct the problem, let us look at a 
slightly modified result, starting with a value of 70 cents, and 
offering no discount. 
 
Total : $0.70 
Tax : $0.03 
Tax+Total: $0.74 
 
In the case of the 70 cent transaction, it isn't just a rounding 
problem. Looking at the values without formatting, here's the 
output: 
 
Total : 0.7 
Tax : 0.034999999999999996 
Tax+Total: 0.735 
 
For the sales tax the value 0.035 just can't be stored as a double. 
It just isn't representable in binary form as a double. 
 
The BigDecimal class helps solve some problems with doing 
floating-point operations with float and double. The BigDecimal 
class stores floating-point numbers with practically unlimited 
precision. To manipulate the data, you call the add(value), 
subtract(value), multiply(value), or divide(value, scale, 
roundingMode) methods. 
 
To output BigDecimal values, set the scale and rounding mode with 
setScale(scale, roundingMode), or use either the toString() or 
toPlainString() methods. The toString() method may use scientific 
notation while toPlainString() never will. 
 
Before converting the program to use BigDecimal, it is important to 
point out how to create one. There are 16 constructors for the 
class. Since you can't necessarily store the value of a BigDecimal 
in a primitive object like a double, it is best to create your 
BigDecimal objects from a String. To demonstrate this error, here's 
a simple example: 
 
  double dd = .35; 
  BigDecimal d = new BigDecimal(dd); 
  System.out.println(".35 = " + d); 
 
The output is not what you might have expected: 
 
  .35 = 0.34999999999999997779553950749686919152736663818359375 
 
Instead, what you should do is create the BigDecimal directly with 
the string ".35" as shown here: 
 
  BigDecimal d = new BigDecimal(".35"); 
 
resulting in the following output: 
 
  .35 = 0.35 
 
After creating the value, you can explicitly set the scale of the 
number and its rounding mode with setScale(). Like other Number 
subclasses in the Java platform, BigDecimal is immutable, so if you 
call setScale(), you must "save" the return value: 
 
  d = d.setScale(2, RoundingMode.HALF_UP); 
 
The modified program using BigDecimal is shown here. Each 
calculation requires working with another BigDecimal and setting its 
scale to ensure the math operations work for dollars and cents. If 
you want to deal with partial pennies, you can certainly go to three 
decimal places in the scale but it isn't necessarily. 
 
import java.math.BigDecimal; 
import java.math.RoundingMode; 
 
public class Calc2 { 
  public static void main(String args[]) { 
    BigDecimal amount = new BigDecimal("100.05"); 
    BigDecimal discountPercent = new BigDecimal("0.10"); 
    BigDecimal discount = amount.multiply(discountPercent); 
    discount = discount.setScale(2, RoundingMode.HALF_UP); 
    BigDecimal total = amount.subtract(discount); 
    total = total.setScale(2, RoundingMode.HALF_UP); 
    BigDecimal taxPercent = new BigDecimal("0.05"); 
    BigDecimal tax = total.multiply(taxPercent); 
    tax = tax.setScale(2, RoundingMode.HALF_UP); 
    BigDecimal taxedTotal = total.add(tax); 
    taxedTotal = taxedTotal.setScale(2, RoundingMode.HALF_UP); 
    System.out.println("Subtotal : " + amount); 
    System.out.println("Discount : " + discount); 
    System.out.println("Total : " + total); 
    System.out.println("Tax : " + tax); 
    System.out.println("Tax+Total: " + taxedTotal); 
  } 
} 
 
Notice that NumberFormat isn't used here, though you can add it back 
if you'd like to show the currency symbol. 
 
Now, when you run the program, the calculations look a whole 
lot better: 
 
Subtotal : 100.05 
Discount : 10.01 
Total : 90.04 
Tax : 4.50 
Tax+Total: 94.54 
 
BigDecimal offers more functionality than what these examples 
show. There is also a BigInteger class for when you need unlimited 
precision using whole numbers. The Java platform documentation for 
the two classes offers more details for the two classes, including 
more details on scales, the MathContext class, sorting, and 
equality. 
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
DEVELOPER ASSISTANCE 
 
Need programming advice on Java SE? Try Developer Expert Assistance 
http://communications1.sun.com/r/c/r?2.1.3J1.2Vc.11Hawu.C43uNM..T.Eazw.1kEg.FAaEdR00 
 
. . . . . . . . . . . . . . . . . . . . . . . 
 
(c) 2007 Sun Microsystems, Inc. All Rights Reserved. For information 
on Sun's trademarks see: http://www.sun.com/suntrademarks 
 
FEEDBACK Comments? Send your feedback on to 
[email-masked]. 
 
To unsubscribe from this list, reply to this message with 
"Unsubscribe" in the subject line or use the link below: 
http://communications1.sun.com/r/c/r?2.1.3J1.2Vc.11Hawu.C43uNM..T.Eaz%5f.1kEg.CdKEcZ00 
 
To manage your Sun subscriptions, visit the Subscription Center: 
http://communications1.sun.com/r/c/r?2.1.3J1.2Vc.11Hawu.C43uNM..T.Ea%5f0.1kEg.DcKSEaP0 
 
Sun Microsystems, Inc., 10 Network Circle, MS: MPK10-209, Menlo 
Park, CA 94025 USA