Apuntes Lenguaje Java

19. Interfaces

19.1. Concepto de Interface
19.2. Declaración y uso
19.3. Referencias a Interfaces
19.4. Extensión de Interfaces
19.5. Agrupación de Constantes
19.6. Un ejemplo casi real

19.1. Concepto de Interface

El concepto de Interface lleva un paso más adelante la idea de las clases abstractas. En Java una interface es una clase abstracta pura, es dcir una clase donde todos los métodos son abstractos (no se implementa ninguno). Permite al diseñador de clases establecer la forma de una clase (nombres de métodos, listas de argumentos y tipos de retorno, pero no bloques de código). Una interface puede también contener datos miembro, pero estos son siempre static y final. Una interface sirve para establecer un 'protocolo' entre clases. 

Para crear una interface, se utiliza la palabra clave interface en lugar de class. La interface puede definirse public o sin modificador de acceso, y tiene el mismo significado que para las clases. Todos los métodos que declara una interface son siempre public. 

Para indicar que una clase implementa los métodos de una interface se utiliza la palabra clave implements. El compilador se encargará de verificar que la clase efectivamente declare e implemente todos los métodos de la interface. Una clase puede implementar más de una interface.

19.2. Declaración y uso

Una interface se declara:

interface nombre_interface {
    tipo_retorno nombre_metodo ( lista_argumentos ) ;
    . . . 
}

Por ejemplo:

interface InstrumentoMusical {
    void tocar();
    void afinar();
    String tipoInstrumento();
}

Y una clase que implementa la interface:

class InstrumentoViento extends Object implements InstrumentoMusical {
    void tocar() { . . . };
    void afinar() { . . .};
    String tipoInstrumento() {}
}

class Guitarra extends InstrumentoViento {
    String tipoInstrumento() {
        return "Guitarra";
    }
}   

La clase InstrumentoViento implementa la interface, declarando los métodos y escribiendo el código correspondiente. Una clase derivada puede también redefinir si es necesario alguno de los métodos de la interface. 

19.3. Referencias a Interfaces

Es posible crear referencias a interfaces, pero las interfaces no pueden ser instanciadas. Una referencia a una interface puede ser asignada a cualquier objeto que implemente la interface. Por ejemplo:

InstrumentoMusical instrumento = new Guitarra();
instrumento.play();
System.out.prinln(instrumento.tipoInstrumento());

InstrumentoMusical i2 = new InstrumentoMusical(); //error.No se puede instanciar

19.4. Extensión de interfaces

Las interfaces pueden extender otras interfaces y, a diferencia de las clases, una interface puede extender más de una interface. La sintaxis es:

interface nombre_interface  extends nombre_interface  , . . . {
    tipo_retorno nombre_metodo ( lista_argumentos ) ;
    . . . 
}

19.5. Agrupaciones de constantes

Dado que, por definición, todos los datos miembros que se definen en una interface son static y final, y dado que las interfaces no pueden instanciarse resultan una buena herramienta para implantar grupos de constantes. Por ejemplo:

public interface Meses {
    int ENERO = 1 , FEBRERO = 2 . . . ;
    String [] NOMBRES_MESES = { " " , "Enero" , "Febrero" , . . . };
}

Esto puede usarse simplemente:

System.out.println(Meses.NOMBRES_MESES[ENERO]);

19.6. Un ejemplo casi real

El ejemplo mostrado a continuación es una simplificación de como funciona realmente la gestión de eventos en el sistema gráfico de usuario soportado por el API de Java (AWT o swing). Se han cambiado los nombres y se ha simplificado para mostrar un caso real en que el uso de interfaces resuelve un problema concreto.

Supongamos que tenemos una clase que representa un botón de acción en un entorno gráfico de usuario (el típico botón de confirmación de una acción o de cancelación). Esta clase pertenecerá a una amplia jerarquía de clases y tendrá mecanismos complejos de definición y uso que no son objeto del ejemplo. Sin embargo podríamos pensar que  la clase Boton tiene miembros como los siguientes.

class Boton extends . . . {
    protected int x , y, ancho, alto; // posicion del boton
    protected String texto;  // texto del boton
    Boton(. . .) {
        . . .
    }
    void dibujar() { . . .}
    public void asignarTexto(String t) { . . .}
    public String obtenerTexto() { . . .)
    . . .
}   

Lo que aquí nos interesa es ver lo que sucede cuando el usuario, utilizando el ratón pulsa sobre el botón. Supongamos que la clase Boton tiene un método, de nombre por ejemplo click(), que es invocado por el gestor de ventanas cuando ha detectado que el usuario ha pulsado el botón del ratón sobre él. El botón deberá realizar alguna acción como dibujarse en posición 'pulsado' (si tiene efectos de tres dimensiones) y además, probablemente, querrá informar a alguien de que se ha producido la acción del usuario. Es en este mecanismo de 'notificación' donde entra el concepto de interface. Para ello definimos una interface Oyente de la siguiente forma:

interface Oyente {
    void botonPulsado(Boton b);
}

La interface define un único método botonPulsado. La idea es que este método sea invocado por la clase Boton cuando el usuario pulse el botón. Para que esto sea posible en algún momento hay que notificar al Boton quien es el Oyente que debe ser notificado. La clase Boton quedaría:

class Boton extends . . . {
    . . .
    private Oyente oyente;
    void registrarOyente(Oyente o) {
        oyente = o;
    }
    void click() {
        . . .
        oyente.botonPulsado(this);
    }
}

El método registrarOyente sirve para que alguien pueda 'apuntarse' como receptor de las acciones del usuario. Obsérvese que existe una referencia de tipo Oyente. A Boton no le importa que clase va a recibir su notificación. Simplemente le importa que implante la interface Oyente para poder invocar el método botonPulsado. En el método click se invoca este método. En el ejemplo se le pasa como parámetro una referencia al propio objeto Boton. En la realidad lo que se pasa es un objeto 'Evento' con información detallada de lo que ha ocurrido.

Con todo esto la clase que utiliza este mecanismo podría tener el siguiente aspecto:

class miAplicacion extends . . . implements Oyente {
    public static main(String [] args) {
        new miAplicacion(. . .); 
        . . .
    }
     . . . 
    miAplicacion(. . .) {
        . . .
        Boton b = new Boton(. . .);
        b.registrarOyente(this);
    }

    . . .
    void botonPulsado(Boton x) {
        // procesar click
        . . .
    }
}

Obsérvese en el método registrarOyente que se pasa la referencia thisque en el lado de la clase Boton es recogido como una referencia a la interface Oyente. Esto es posible porque la clase miAplicacion implementa la interface Oyente . En términos clásicos de herencia miAplicacion ES un Oyente .

Antonio Bel Puchol - antonio@abelp.net