Tutorial de Java

Clases Anidadas

Anterior | Siguiente
  1. Clases Anidadas
  2. Aplicación de Control

Una de las características incorporadas a Java en el JDK 1.1 ha sido la capacidad de crear clases anidadas, y también el uso de sintaxis abreviada (aunque un tanto críptica) para la definición de clases anónimas y la instanciación de objetos anónimos desde esas clases.

A continuación se presentan dos ejemplos para ilustrar estos conceptos. En el primero de ellos se implementan clases anidadas, pero no se utilizan objetos anónimos , y en el segundo ejemplo se utilizan clases anónimas y sintaxis abreviada, modificando en primer ejemplo.

Java, en versiones anteriores soportaba solamente clases top-level, que tenían que ser miembros de paquetes. A partir del JDK 1.1, el programador puede definir clases anidadas como miembros de otras clases, es decir, colocar la definición de una clase dentro de la definición de otra clase, bien localmente, dentro de un bloque de sentencias o, anónimamente, dentro de una expresión.

La verdad es que la necesidad de las clases anidadas no es demasiado obvia en un principio, así que a continuación se exponen algunos ejemplos que aclararán el uso y beneficios de la utilización de las clases anidadas.

El ejemplo java1201.java, hace uso de clases anidadas de una forma sencilla, definiendo clases dentro de clases y utilizando esas definiciones para instanciar los objetos que necesita. Esto difiere significativamente de las anteriores secciones en que se hablaba del Modelo de Delegación de Eventos, donde las clases necesarias para instanciar los objetos Receptor eran clases top-level, es decir, subclases directas de la clase Object o subclases de una clase adaptadora como MouseAdapter. En este programa, las clases necesarias para instancias los objetos receptores de eventos, Listener, así como algunos otros, están dentro de otras clases.

import java.awt.*;
import java.awt.event.*;
    
public class java1201 {
    void cantar() {
        System.out.println( "Cantando: Ese toro enamorado de la luna..." );
        }
    
    void silbar() {
        System.out.println( "Silbando: Fiuuu, Fiiuuu, Fiiiuuu..." );
        }
    
    static public void main( String args[] ) {
        java1201 app = new java1201();
        IHM ihm = app.new IHM(); // Obsérvese la sintaxis aquí
        }

    // Esta clase IHM esta definida dentro de la clase java1201
    class IHM extends Frame {
        // Esta clase CantarActionListener esta definida dentro de la clase IHM
        class CantarActionListener implements ActionListener {
            public void actionPerformed( ActionEvent evt ) {
                cantar();
                }
            }
    
        // Esta clase SilbarActionListener esta definida dentro de la clase IHM
        class SilbarActionListener implements ActionListener {
            public void actionPerformed( ActionEvent evt ) {
                silbar();
                }
            }
    
        // Esta clase Conclusion esta definida dentro de la clase IHM
        class Conclusion extends WindowAdapter {
            public void windowClosing( WindowEvent evt ) {
                System.exit( 0 );
                }
            }

        // Constructor de la clase
        public IHM() {
            setLayout( new FlowLayout() );
            setTitle( "Tutorial de Java, Clases Anidadas" );
    
            Button botonCantar;
            add( botonCantar = new Button( "Cantar" ) );
            botonCantar.addActionListener( new CantarActionListener() );
    
            Button botonSilbar;
            add( botonSilbar = new Button( "Silbar" ) );
            botonSilbar.addActionListener( new SilbarActionListener() );
    
            // Registra un receptor para los eventos del Frame de IHM
            addWindowListener( new Conclusion() ); 
            setSize( 300,75 );
            setVisible( true );
            }
        }
    }

La clase principal es java1201. Una clase anidada IHM está definida dentro de java1201 y un objeto de esta clases es instanciado dentro del método main().

Además, hay dos métodos, cantar() y silbar(), que están definidos dentro de la clase principal. Están definidos separadamente del controlador de eventos, aunque sean invocados por métodos de ese controlador.

Hay tres clases adicionales definidas dentro de la clase IHM, que son CantarActionListener, SilbarActionListener y Conclusion. Las tres son clases Listener, receptores de eventos; las dos primeras implementan el interfaz ActionListener para control de los eventos semánticos de tipo Action, mientras que la tercera extiende el adaptador WindowListener.

Aunque este ejemplo no utiliza clases anónimas, sí utiliza objetos anónimos. Se instancian objetos anónimos Listener de las clases CantarActionListener y SilbarActionListener, que son registrados para manejar los eventos de los dos botones que aparecen en el interfaz gráfico. Además, se registra otro objeto anónimo de la clase Conclusion para manejar los eventos que se produzcan sobre el objeto Frame de la clase IHM.

El siguiente programa, java1202.java, es similar al que se acaba de describir, excepto que hace uso de clases anónimas para instanciar los objetos Listener.

Para que las clases adaptadoras sean lo más concisas posible, el JDK 1.1 permite la utilización de una notación abreviada para objetos locales, de tal modo que en una sola expresión se combina la definición de una clase anónima y la creación de una instancia de esa clase.

En este ejemplo, tal circunstancia se ilustra en el trozo de código que se reproduce en las siguientes líneas:

botonCantar.addActionListener(
    new ActionListener() {
        public void actionPerformed( ActionEvent evt ) {
            cantar();
            }
        }
    );

En este caso, el código define una nueva clase anónima que automáticamente implementa el interfaz ActionListener (sin utilizar la palabra clave implements) e instancia un objeto anónimo de esa nueva clase. Se indica que son anónimos porque ni la clase ni el objeto tienen un nombre asignado.

Otro aspecto interesante de la sintaxis que se puede observar en el código anterior es la línea:

    new ActionListener() {

que es semejante a la llamada a un constructor en una instanciación normal de objetos. Sin embargo, ActionListener no es una clase, es un interfaz, y como tal, no contiene métodos totalmente definidos, por lo que aparentemente parece que no debiera tener constructor alguno.

Hay que prestar especial atención a la posición de los paréntesis de apertura y cierre que definen la lista de argumentos del método addActionListener(). La definición de la clase anónima y la instanciación del objeto anónimo están incluidos en esa lista de argumentos.

Cuando se utiliza esta notación abreviada, una expresión con el operador new puede constituir el cuerpo de una clase. Es importante darse cuenta de que una clase anónima tiene inicializadores, pero no constructores. La lista de argumentos asociada a la expresión new es pasada implícitamente al constructor de la superclase.

import java.awt.*;
import java.awt.event.*;
    
public class java1202 {
    void cantar() {
        System.out.println( "Cantando: Ese toro enamorado de la luna..." );
        }
    
    void silbar() {
        System.out.println( "Silbando: Fiuuu, Fiiuuu, Fiiiuuu..." );
        }
    
    static public void main( String args[] ) {
        java1202 app = new java1202();
        IHM ihm = app.new IHM(); // Obsérvese la sintaxis aquí
        }

    // Esta clase IHM esta definida dentro de la clase java1202
    class IHM extends Frame {
        // Constructor de la clase
        public IHM() {
            setLayout( new FlowLayout() );
            setTitle( "Tutorial de Java, Clases Anidadas" );
    
            Button botonCantar;
            add( botonCantar = new Button("Cantar") );
            Button botonSilbar;
            add( botonSilbar = new Button("Silbar") );
            // En el siguiente código se instancian tres objetos anónimos de
            // tipos ActionListener y WindowListener, y los registra para 
            // controlar los eventos que se generen en los correspondientes
            // objetos Button y en el objeto Frame.
            // El código utiliza sintaxis abreviada
            botonCantar.addActionListener(
                new ActionListener() {
                    public void actionPerformed( ActionEvent evt ) {
                        cantar();
                        }
                    }
                );
    
            botonSilbar.addActionListener(
                new ActionListener() {
                    public void actionPerformed( ActionEvent evt ) {
                        silbar();
                        }
                    }
                );
    
            addWindowListener(
                new WindowAdapter() {
                    public void windowClosing( WindowEvent evt ) {
                        System.exit( 0 );
                        }
                    }
                );
    
            setSize( 300,100 );
            setVisible( true );
            }
        }
    }

El funcionamiento de este ejemplo es similar al anterior programa, y en el código solamente se aprecia la diferencia de la notación abreviada utilizada para definir las clases y para instanciar los tres objetos receptores de eventos dentro de la clase anidada IHM.



Navegador

Home | Anterior | Siguiente | Indice | Correo