Sign In/My Account | View Cart  

advertisement

AddThis Social Bookmark Button

Listen Print Discuss

Seven Low-Cost Ways to Improve Legacy Code
Pages: 1, 2

6. Replace Listeners with Weak Listeners

Chapter 11 ("References in Four Flavors") of Hardcore Java illustrates a recurring problem in many Java programs, namely the creation of circular reference trees that pin objects in memory and interfere with garbage collection. The widely held misconception that a "Java developer does not have to worry about the memory of his program" has led many Java developers to create programs that require excessive amounts of resources. The extra resources consumed by objects unintentionally pinned in memory are Java's version of a memory leak.



This kind of memory leak can be avoided by the use of weak references and weak listeners. Weak references are a special kind of reference that do not block garbage collection. This allows your data objects to hold references to GUI panels in their property change listeners without blocking the garbage collection of those panels. Furthermore, it relieves the user of the data object from having to constantly manage the addition and removal of listeners, which results in code that is easier to manage. Legacy programs can be easily converted to use weak listeners. To show how these programs can be converted, consider the following legacy code:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SomeButtonClass1 {
    private final Set listeners = new HashSet();
    public void addActionListener(final ActionListener l) {
        listeners.add(l);
    }
    public void removeActionListener(final ActionListener l) {
        listeners.remove(l);
    }
    protected void fireActionPerformed(final ActionEvent event) {
        for (final Iterator iter = listeners.iterator(); 
             iter.hasNext();) {
            ((ActionListener)iter).actionPerformed(event);
        }
    }
}

This code can easily be converted to use weak listeners with a couple of minor changes:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;

public class SomeButtonClass {
    private final Map listeners = new WeakHashMap();
    public void addActionListener(final ActionListener l) {
        listeners.put(l, null);
    }
    public void removeActionListener(final ActionListener l) {
        listeners.remove(l);
    }
    protected void fireActionPerformed(final ActionEvent event) {
        for (final Iterator iter = listeners.keySet().iterator();
             iter.hasNext();) {
            ((ActionListener)iter).actionPerformed(event);
        }
    }
}

After these three small changes, the listeners don't even need to bother with removing themselves, if they don't wish to. If they remove themselves, all will be OK. If they are merely garbage collected, all will still be OK. To understand how this works in more detail, I encourage you to read Chapter 11 of Hardcore Java.

Hardcore Java

Related Reading

Hardcore Java
By Robert Simmons, Jr.

7. Replace Integer Constants with Constant Objects or Enums

Chapter 7 ("All About Constants") of Hardcore Java talks about constants in detail. One of the most important lessons you should carry out of that chapter is that using integers for option constants is a bad idea. For example, if you want to create a class that can only take a color of the rainbow as an argument to a method, using integers for each of the seven colors is the wrong approach. We show an example of such code here:

public final class RainbowColor {
    public final static int RED = 0;
    public final static int ORANGE = 1;
    public final static int YELLOW = 2;
    public final static int GREEN = 3;
    public final static int BLUE = 4;
    public final static int INDIGO = 5;
    public final static int VIOLET = 6;
}

When using integer constants such as these, you might write the following code:


public void doSomething(final int rainbowColor) {
       // ...
     }

The problem here is that the user can pass you any integer. In order to make solid code, you will have to check that integer in every piece of code against the valid integers. This checking code will have to be maintained in several places. On the other hand, the use of Enums or Constant Objects relieves you of the need to check, as we see in the revised option constant class:

public final class RainbowColor {
    public final static RainbowColor RED = new RainbowColor("RED");
    public final static RainbowColor ORANGE = new RainbowColor("ORANGE");
    public final static RainbowColor YELLOW = new RainbowColor("YELLOW");
    public final static RainbowColor GREEN = new RainbowColor("GREEN");
    public final static RainbowColor BLUE = new RainbowColor("BLUE");
    public final static RainbowColor INDIGO = new RainbowColor("INDIGO");
    public final static RainbowColor VIOLET = new RainbowColor("VIOLET");

    final String name;

    private RainbowColor(final String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

With the revised class, no checking is necessary during its use:

public void doSomething(final RainbowColor rainbowColor) {
  // ...
}

Since the user cannot create any more instances of the RainbowColor, there is no need to check anything. Furthermore, since comparisons of the objects are done by instance instead of by equality, comparisons are extremely fast. If you have access to JDK 1.5 in your coding environment, enums are the equivalent of Constant objects, with less code needed from the developer. If you are stuck writing for an earlier JDK (as many corporate programmers are), you should investigate and use the Constant object pattern extensively.

Conclusion

These seven techniques are fairly low cost in terms of man hours, and all are cost-free in terms of equipment and software. They can all be performed by hand or with freely available software. Although they represent only the tip of the iceberg of code-quality improvement, they offer a good place to start when time is tight and quality demands are high. Given the tools at the disposal of software engineers, there should be no excuse for writing code that is anything other than solid as stone. This will allow you to concentrate less on finding little annoying typos and more on the business your customers or employers use to make money. They will be happier with your code, and you will have to work less to accomplish the same tasks.

Robert Simmons, Jr. lives and works as a senior software architect in Germany. He is the author of O'Reilly's Hardcore Java.


O'Reilly Media recently released Hardcore Java.


Return to ONJava.com.


  • PMD and jEnum
    2004-05-03 08:57:48  stinkyminky [Reply | View]

    I am a fan of IntelliJ.

    However, if you are looking for code-inspection tool, you should check out PMD. http://pmd.sourceforge.net/

    One sub-project of PMD is CPD [ cut&paste dectactor ]. It is a pretty neat tool.

    As for Enum, it is not easy to write correct implementation of it. Serialization, '==' and other issues comes into the play. Most of the issues have been highlighted in Joshua Bloch's Effective Java book. However, you can use 'jEnum' to create for you.

    http://jenum.sourceforge.net/

    For IntelliJ, I created the external tools for 'PMD' and 'jEnum'. With a couple of cliks, I can check my code and create Enum class. Hack - I even put JAXB binding as the external tools.

    Life is beautiful with IntelliJ....
    • PMD and jEnum
      2004-05-03 16:44:46  Robert Simmons Jr. (Kraythe) [Reply | View]

      I address serialization of Constant Objects in my book. It basically involves using the read Resolve method. It is quite easy especially since you can put all of the technical stuff in the base class.
  • JLint
    2004-04-30 07:56:51  rasmusmc [Reply | View]

    Won't JLint solve many of the strong compiler tool problems without having ot change the compiler you use?
    • JLint
      2004-04-30 15:34:36  Robert Simmons Jr. (Kraythe) [Reply | View]

      I have never used JLint. However, I would merely check to see what kind of settings it can manage. So far everything I have seen in eclipse is pretty good (even too much in some places).

      However, whatever tool you use, the most important thing is that it is part of your development process. You should be runnign this tool every time you compile. Tools that are run once a week or once a month often find many things but people dont want to go back and fix things so they are soon neglected. This is one reason I favor the integration of such warnings into compilers.
  • final checks
    2004-04-29 07:28:12  simonharris1 [Reply | View]

    Checkstyle does have a check to ensure you don't reassign the value of a parameter. This means you can avoid having to actually write final everywhere in your code but still have the benefits.
    • final checks
      2004-05-02 06:49:11  Steve Peters [Reply | View]

      Neither does java have a check to make sure that you use Checkstyle on your code nor doesjava enforce this on all users of your classes. Adding finals to your code ensures that your code works no matter who how it is built.
    • final checks
      2004-04-29 16:19:35  Robert Simmons Jr. (Kraythe) [Reply | View]

      There is absolutely zero negative impact to making extensive use of final. Therefore, its of zero cost to use it a lot. If I had a dollar for every bug that technique has caught, I could retire.

      Second of all, never hang your code base on any one tool. Its a really bad idea, especailly if you go to a company that doesnt use that tool and doesnt want to use it.
  • using final with Serializable classes
    2004-04-29 00:58:42  andyp [Reply | View]

    I have a question about declaring member variables to be final in immutable classes. I agree with you that it is a trick to help the compiler enforce your own logic about the class. However when it comes to serializing these classes there is a problem. If I implement the serialization method readObject(ObjectOutputStream os), then all of the member variables that I will be setting in this method must be non-final. Do you have any suggestinos about how to have final methods in this case (without using readResolve)?
    • using final with Serializable classes
      2004-04-29 16:20:44  Robert Simmons Jr. (Kraythe) [Reply | View]

      Hmm, interesting question. However, since String uses final for its data member, I immagine the problem is solvable. I will have to play around with it.

      I will get back to you. =)
      • using final with Serializable classes
        2005-04-13 14:34:32  Mith [Reply | View]

        Try this on for size:

        import java.io.Serializable;

        /**
        * "Typesafe enum" for the different document indicators associated with
        * a transaction.
        *


        * Copyright 2003 Southwest Airlines
        */

        public final class DocumentIndicator implements Serializable, Comparable
        {
        private static int nextOrdinal = 0;

        private int ordinal;
        private transient String name;

        public static final DocumentIndicator SALES =
        new DocumentIndicator("NA");
        public static final DocumentIndicator SALES_EXCHANGES =
        new DocumentIndicator("NE");
        public static final DocumentIndicator POSITIVE_ADJUSTMENTS =
        new DocumentIndicator("NY");
        public static final DocumentIndicator TICKET_BY_MAIL_SALES =
        new DocumentIndicator("TA");
        public static final DocumentIndicator REFUNDS =
        new DocumentIndicator("NR");
        public static final DocumentIndicator NEGATIVE_ADJUSTMENTS =
        new DocumentIndicator("NX");
        public static final DocumentIndicator TICKET_BY_MAIL_REFUNDS =
        new DocumentIndicator("TR");
        public static final DocumentIndicator OLD_REFUNDS =
        new DocumentIndicator("RF");

        private static final DocumentIndicator[] PRIVATE_VALUES =
        new DocumentIndicator[]
        {
        SALES, SALES_EXCHANGES, POSITIVE_ADJUSTMENTS, TICKET_BY_MAIL_SALES,
        REFUNDS, NEGATIVE_ADJUSTMENTS, TICKET_BY_MAIL_REFUNDS, OLD_REFUNDS
        };
        /**
        * All instances are controlled by the class.
        */

        private DocumentIndicator(String name)
        {
        this.name = name;
        this.ordinal = nextOrdinal++;
        }
        public int compareTo(Object o)
        {
        DocumentIndicator other = (DocumentIndicator) o;

        return name.compareTo(other.name);
        }
        /**
        * Returns the DocumentIndicator associated with the given string.
        *
        * @param indicator the String to try to match against the document
        * indicators
        * @return the DocumentIndicator associated with the given String
        */

        public static DocumentIndicator getIndicatorFrom(String indicator)
        {
        if ("NA".equals(indicator))
        {
        return SALES;
        }
        else if ("NE".equals(indicator))
        {
        return SALES_EXCHANGES;
        }
        else if ("NY".equals(indicator))
        {
        return POSITIVE_ADJUSTMENTS;
        }
        else if ("TA".equals(indicator))
        {
        return TICKET_BY_MAIL_SALES;
        }
        else if ("NR".equals(indicator))
        {
        return REFUNDS;
        }
        else if ("NX".equals(indicator))
        {
        return NEGATIVE_ADJUSTMENTS;
        }
        else if ("TR".equals(indicator))
        {
        return TICKET_BY_MAIL_REFUNDS;
        }
        else if ("RF".equals(indicator))
        {
        return OLD_REFUNDS;
        }
        else
        {
        return null;
        }
        }
        private Object readResolve() throws java.io.ObjectStreamException
        {
        return PRIVATE_VALUES[ordinal];
        }
        /**
        * Returns a String that represents the value of this object.
        *
        * @return a string representation of the receiver
        */

        public String toString()
        {
        return name;
        }
        }

      • using final with Serializable classes
        2004-04-29 16:22:38  Robert Simmons Jr. (Kraythe) [Reply | View]

        Incidentally, for those that dont know, I am Robert Simmons and Kraythe. Kraythe is my internet nickname.
  • IntelliJ IDEA
    2004-04-28 19:39:49  michaelcherichetti [Reply | View]

    IntelliJ IDEA can help out with 4 of these:

    1. Picks up on unused variables as you code. The code inspector can find them too.

    2. Built in formatter.

    3. The code inspector can find things that will be final and walk you through applying final to everything you want. It's slick as are a lot of other things in the code inspector.

    5. Has a "Convert Anonymous Class to Inner" refactoring.
    • IntelliJ IDEA
      2004-04-30 07:20:35  boriskraft [Reply | View]

      Besides using the built-in inspector (already mentioned), Idea lets you opt to show (color code) that a local parameter or var can be final. See Settings->Errors for that option.

      Great article by the way, I'll check out your book.
      - Boris
    • IntelliJ IDEA
      2004-04-29 16:25:40  Robert Simmons Jr. (Kraythe) [Reply | View]

      Since I have never used IntelliJ, I will take your word for it. =) However, Eclipse can do these things as well, especially the new M8 version; and for a lower cost.

      However, whichever tool you use, make sure to turn on thos options and use the tools available! =)
      • IntelliJ IDEA
        2004-04-29 18:20:38  michaelcherichetti [Reply | View]

        You should try it, you'll never utter the word Eclipse again if you do, take my word for it :) The only reason I suggested IntelliJ was that you said in #3 that you didn't know of any tools that could automate the process and also said that Eclipse couldn't do it, so can it do it or not?

        Anyway, I agree that the tool doesn't matter as long as it gets the job done, but please don't do yourself the injustice of ruling a tool out just because it costs money. In my opinion, IntelliJ is a *steal* at $499.

        By the way, I love your book. I am a long user of final to prevent logic errors. When someone argues with me nowadays about it I direct them to your book, so thanks for that chapter :)
        • IntelliJ IDEA
          2004-05-01 15:12:17  mikeunicode [Reply | View]

          - I'd like you to give us some clue about how to Set up Eclipse to do this code checking, and publish that as a major article on OnJava.
          I've run into problems attempting to use open source projects( developed with using Eclipse ) with JDeveloper catching errors in the libraries, making them unusable.

          - What version of JBuilder are you denigrating?
          JBuilder X catches more bugs than I care for actually.

          I'm not a big fan of Eclipse.
          Using SWT breaks the Pure Java mantra.
          Is this IBM's smart move to turn Java into a fragmented Unix environment. IBM can then have a Big piece of a much smaller pie.


    • IntelliJ IDEA
      2004-04-28 19:40:35  michaelcherichetti [Reply | View]

      Oops, "that will be final and walk you through" should be "that *can* be final and walk you through"