Listeners vs Adapters

by John Zukowski

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
- ComponentListener/ComponentAdapter
- 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 your self 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.

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.

Comments:

wow man, and i mean WOW! you've reached a level not many of us developers will ever reach... who the f... do you think you're writing to? complete dumbs?!

Posted by god save us all... on July 17, 2007 at 06:35 AM PDT #

To "god save us all...": Hey, not all of us are seasoned developers (yet)! I liked this article. Quite low-level, but sometimes that's nice too.

Posted by newbie on July 17, 2007 at 06:49 PM PDT #

"ComputerListener/ComputerAdapter"? Should that not be Component? I got all excited for a minute there.

Posted by M Graham on July 25, 2007 at 06:49 PM PDT #

Yes, that is a typo. I've changed the blog entry to say ComponentListener and ComponentAdapter.

Posted by John O'Conner on July 26, 2007 at 01:30 AM PDT #

This entry is <strong>too basic</strong> for the average level in this series :(

Posted by Carlos on July 26, 2007 at 04:26 AM PDT #

I want to direct your attention to the danger of using predefined adapters.

If you have a typo (here mousePress instead of mousePressed) when implementing a Listener interface, you will get immediately a compiler warning.

But if you comfortably extend an Adapter class, the typo will not be noticed, since the method is already implemented by the Adapter.

So, if you often need to implement only some specific methods of a multi-method interface, you should better write you own Adapter, leaving unimplemented only these specific methods.

Posted by Christoph Knabe on September 11, 2007 at 08:04 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

John O'Conner

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today