ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button O'Reilly Book Excerpts: Hardcore Java

Nested Classes, Part 2

Editor's note: Robert Simmons continues his efforts to clarify confusion over the use of nested classes in Java in this week's installment, excerpted from Chapter 6 ("Nested Classes") of Hardcore Java. Robert discusses the somewhat troublesome limited-scope inner classes; one specific type within this category, known as anonymous classes; and the various problems programmers can encounter with limited-scope classes.

Limited-Scope Inner Classes

One of the strangest types of inner classes is the limited-scoped inner class. Limited-scoped classes are scoped to a particular block of code. Their declaration and usage all happen within that block. To get a better idea of how limited-scoped inner classes work, see Example 6-4.

Example 6-4. A limited-scope inner class scoped to a method

package oreilly.hcj.nested;
public class MethodInnerClassDemo extends JDialog {

   /** Holds the location of the logo image. */
   private static final String LOGO_LOCATION =
                "oreilly/hcj/nested/oreilly_header3.gif";
   static {
      LOGO = new ImageIcon(ClassLoader.getSystemClassLoader( )
                                      .getResource(LOGO_LOCATION));
   }
   
   /** Holds a reference to the content pane. */
   private final Container contentPane;
 
   /** holds a demo variable. */
   private String demo;

   public MethodInnerClassDemo(final int value) {
      super( );
      String title = "Inner Class Demo";
      setTitle(title);
      setModal(true);
      contentPane = getContentPane( );
      contentPane.setLayout(new BorderLayout( ));

      JLabel logoLabel = new JLabel(LOGO);
      contentPane.add(BorderLayout.NORTH, logoLabel);

      JButton btn = new JButton("Beep");

      class MyActionListener implements ActionListener {
         public void actionPerformed(final ActionEvent event) {
            Toolkit.getDefaultToolkit().beep( );
            System.out.println(value);
            System.out.println(LOGO_LOCATION);
            System.out.println(MethodInnerClassDemo.this.demo);
         }
      }
      btn.addActionListener(new MyActionListener( ));
	  
      contentPane.add(BorderLayout.SOUTH, btn);
      pack( );
   }
}

Other than the fact that the declaration of the class occurs within the body of a method, this looks like any other class declaration. You can even use the keywords final and abstract on these declarations and develop class hierarchies all within one limited block. Once declared,you can use the class throughout the remainder of the method just like any other class. However,there are some important differences between limited-scope inner classes and normal classes.

Hardcore Java

Related Reading

Hardcore Java
By Robert Simmons, Jr.

First of all,limited-scope inner classes can access final variables of the declaring block. In Example 6-4, the actionPerformed( ) method of MyActionListener uses the variable value despite the fact that it hasn't declared this variable. In this case, value is a final parameter to the method; therefore, MyActionListener can access it. However, MyActionListener would not have access to the local variable title, as it is not final.

In addition to final variables of the enclosing block, a limited-scope inner class can access all members of the enclosing class,including the private variables. In Example 6-4, the LOGO_LOCATION and demo variables are class members; therefore, MyActionListener can use them within its methods despite the fact that both are private. Note especially the syntax used to access the demo variable in the outer declaring class:

System.out.println(MethodInnerClassDemo.this.demo);

This is the proper way to access an instance variable of an outer class from an inner class. The syntax specifies which this pointer to use, which is critical when your inner class has a variable with the same name as a variable in the outer class. If you use this syntax without the type specifier, you will get access only to the variable in the inner class.

Additionally, the limited-scope inner classes are subject to scoping rules that are unique to these classes. Example 6-5 shows a class that has several limited-scope inner classes.

Example 6-5. Various limited-scope inner classes

package oreilly.hcj.nested;
public class MethodInnerClassDemo extends JDialog {

   public MethodInnerClassDemo(final int value) {
      super( );
      String title = "Inner Class Demo";
      setTitle(title);
      setModal(true);
      contentPane = getContentPane( );
      contentPane.setLayout(new BorderLayout( ));
	  
	  JLabel logoLabel = new JLabel(LOGO);
      contentPane.add(BorderLayout.NORTH, logoLabel);
	  
	  JButton btn = new JButton("Beep");
	  
	  class MyActionListener implements ActionListener {
         public void actionPerformed(final ActionEvent event) {
            Toolkit.getDefaultToolkit().beep( );
            System.out.println(value);
            System.out.println(LOGO_LOCATION);
            System.out.println(MethodInnerClassDemo.this.demo);
            // System.out.println(title); // <= compiler error
         }
      }
      btn.addActionListener(new MyActionListener( ));
	  contentPane.add(BorderLayout.SOUTH, btn);
      pack( );
   }
   
   public MethodInnerClassDemo( ) {
      super( );
      setTitle("Inner Class Demo");
      setModal(true);
      contentPane = getContentPane( );
      contentPane.setLayout(new BorderLayout( ));
	  
	  JLabel logoLabel = new JLabel(LOGO);
      contentPane.add(BorderLayout.NORTH, logoLabel);
	  
	  JButton btn1 = new JButton("Beep");
      JButton btn2 = new JButton("Bell");
	  
	  class MyActionListener implements ActionListener {
         public void actionPerformed(final ActionEvent event) {
            Toolkit.getDefaultToolkit().beep( );
         }
      }
      btn1.addActionListener(new MyActionListener( ));
      btn2.addActionListener(new MyActionListener( ));
	  
	  JPanel pnl = new JPanel(new GridLayout(1, 2));
      pnl.add(btn1);
      pnl.add(btn2);
	  
	  contentPane.add(BorderLayout.SOUTH, pnl);
      pack( );
   }
   
   public void someMethod( ) {
      ActionListener listener =
      new MyActionListener( ); // <= compiler error.
   }
}

In Example 6-5,you declare two different limited-scope inner classes in two different constructors. Each of these classes is in scope only within the method in which it is declared. Therefore, the MyActionListener class in the first constructor is a different class than the one in the second constructor despite the fact that they both have the same name. Furthermore, attempting to use one of the MyActionListener classes outside the method, which is done in someMethod( ), will result in a compiler error - specifically, an undeclared type error.

These classes don't necessarily need to be scoped to a method. You could have surrounded them with curly braces, thereby scoping them to a particular block. In fact, many Java GUIs employ a specific type of limited-scope inner class, which is scoped only to a specific method call or assignment. These classes are called anonymous classes.

Pages: 1, 2

Next Pagearrow