Interfaces

Uso de Interfaces

Otro elemento o concepto que agregar a nuestro dominio en la orientación de objetos.

Palabras reservadas clave: implements, interface

Surge de la necesidad de indicar a otras clases el “que” y no el “como”. Aspecto muy similar a los métodos abstractos con las clases Abstractas. En esencia, estos métodos abstractos especifican una interfaz del método, pero no su implementación. Son las subclases de la clase abstracta las que se encargaran de proporcionar, cada una, su propia implementación.

Utilidad

  • Propagar funcionalidades a clases sin relación (sin herencia).
  • Comportamientos comunes en grupos de clases pertenecientes a diferentes jerarquías. Ejemplo: Supongamos, por ejemplo, que recibimos información en tiempo real de una serie de estaciones meteorológicas. A partir de dicha información deseamos: mostrarla (app, web,…), generar estadísticas relativas al día en curso (max, mín, promedio,…), realizar predicciones para las próximas horas o días… Clases funcionalmente diferentes pero con algo común, deben actualizarse con cada nueva medida realizada.
  • Simular “herencia múltiple” (la cual con sólo herencia nose puede): una clase podrá implementar más de una interfaz.

Al lío

Declaración:

[public] interface NombreInterfaz [extends InterfazPadre] {
 tipo VAR1 = valor1;
 tipo VAR2 = valor2;
 ...
 [private*
] tipo nombreMetodo1(lista_de_params);
 [private*
] tipo nombreMetodo2(lista_de_params);
}

Aspectos importantes:

  • Una clase deberá implementar todos los métodos de una interfaz.
  • Un interface podrá especificar acceso public o por defecto (si se omite). Si se declara public, debe estar en un fichero con el mismo nombre.
  • Variables: siempre serán constactes y hay que inicializarlas: public+static+final
  • Por lo general todos los métodos son públicos, novedades últimas versiones del JDK:
    • Se pueden crear métodos private (uso interno para la interfaz).
    • Métodos static: se evita la sobreescritura.

Ejemplo de declaración:

public interface Producto {
 double IVA_G = 0.21;
 double IVA_R = 0.10;
 double IVA_SR = 0.04;

 double getPrecio();

 String getNombre();
 
 public static double getTotal(Producto[] lista) {
 
    double sum = 0.0;
    for(Producto p: lista) sum += p.getPrecio();
        return sum;
    }
}

Implementación:

Una vez el interface ha sido declarado, podrá ser implementado por una o más clases. Para ello, la clase incluirá la cláusula implements seguida por el nombre del interface (pueden ser varios separados por comas):

class Nombre [extends Superclase] implements Interface1[, Interface2,...] {
 // Cuerpo de la clase
}

Aspectos importantes:

  • La clase deberá implemetar todos los métodos abstractos del interface (sin cuerpo) y deberá establecer para ellos el modo de acceso public.
  • No está obligada a sobreescribir los métodos por defecto (con cuerpo) del interface. En dicho caso, utilizará la implementación del propio interface.
  • No tiene acceso a los métodos private del interface. Son de uso interno de los métodos con implementaciones por defecto o private del interfaz.
  • No “hereda” ni puede sobreescribir los métodos static del interface. Estos sólo se pueden invocar haciendo uso del propio interface. Producto.metodo()

Ejemplo de implementación:

public class Libro implements Producto {
 
 private final double precio;
 private final String titulo;
 private final int numpag;

 public Libro(String titulo, double precio, int numpag) {
    this.titulo = titulo;
    this.precio = precio;
    this.numpag = numpag;
 }

 @Override
 public double getPrecio() { return this.precio; }
 
 @Override
 public String getNombre() { return this.titulo; }
 
 public int getNumpag() { return this.numpag; }
 
 public class Pelicula implements Producto {
 
 private final double precio;
 private final String titulo;
 private final int durac;
 
 public Pelicula(String titulo, double precio, int durac) {
    this.titulo = titulo;
    this.precio = precio;
    this.durac = durac;
 }

 @Override
 public double getPrecio() { return this.precio; }
 
 @Override
 public String getNombre() { return this.titulo; }
 
 public int getDuracion() { return this.durac; }
}

public class Tienda {
 public static void main(String[] args) {
 
    Libro libro1 = new Libro("Crimen y Castigo", 10.40, 752);
    Pelicula peli1 = new Pelicula("Matrix", 9.99, 150);
    System.out.println(libro1.getNombre() + ", " + libro1.getPrecio() + "€");
    System.out.println(peli1.getNombre() + ", " + peli1.getPrecio() + "€");
}
 

Con Herencia:

  • Un interface puede derivar (extender) de otro interface
  • Como en el caso de la herencia de clases, se empleará la cláusula extends
  • Un interface sólo podrá tener un padre.
  • Una clase que implemente una interface que, a su vez, herede de otro interface, deberá implementar todos los métodos de dicha cadena de herencia (que no tengan un cuerpo por defecto, sean static o private).
  • Una clase podrá implementar cuantos interfaces desee, añadiéndolos a la cláusula implements. Aunque conceptualmente no podemos hablar de herencia múltiple, este mecanismo proporciona a las clases Java cierta capacidad para “incorporar” diferentes comportamientos.

Ejercicios:

Ejemplo: una clase que implementa el interface serie. Para simplificar todas las clases en Unidad4.java

package series;
interface Serie{
    int obtenerSiguiente(); //devuelve el siguiente número de la serie,
    void restablecer(); //reinicia
    void establecerInicio(int x); //establece el valor inicial
}
class MasDos implements Serie{
    int inicio;
    int val;
    MasDos(){
        inicio=0;
        val=0;
    }
    public int obtenerSiguiente(){
        val +=2;
        return val;
    }
    public void restablecer(){
        inicio=0;
        val=0;
    }
    public void establecerInicio(int x){
        inicio=x;
        val=x;
    }
}
class Unidad4{
    public static void main(String[] args) {
        MasDos ob = new MasDos();
        for(int i=0;i<5;i++)
            System.out.println("el siguiente valor es: "+ ob.obtenerSiguiente());
        System.out.println("restableciendo ...");
        ob.restablecer();
        for(int i=0;i<5;i++)
            System.out.println("el siguiente valor es: "+ ob.obtenerSiguiente());
        System.out.println("empezando en 100 ...");
        ob.establecerInicio(100);
        for(int i=0;i<5;i++)
            System.out.println("el siguiente valor es: "+ ob.obtenerSiguiente());
    }
}

Comprueba con el código anterior que:

  • Si quiero declarar el interface como public tengo que escribirlo en otro fichero. Recuerda que un fichero .java sólo puede contener una clase/interface public, y que si existe una clase o interface public su nombre debe coincidir con el nombre del fichero.
  • Recuerda que en un interface un método siempre es público y que en su declaración se ponga o no public un método siempre es public. Pero ojo, en su implementación también tiene que ser public pero hay poner el public explícitamente de forma obligatoria. Observa por ejemplo el error al retirar el acceso public a obtenerSiguiente().

Ejercicio 1:

El main es ahora:

class Unidad4{
    public static void main(String[] args) {
        MasDos ob2 = new MasDos();
        MasTres ob3 = new MasTres();

        System.out.println("De dos en dos ...");
        for(int i=0;i<5;i++)
        System.out.println("el siguiente valor es: "+ ob2.obtenerSiguiente());

        System.out.println("De tres en tres ...");
        for(int i=0;i<5;i++)
        System.out.println("el siguiente valor es: "+ ob3.obtenerSiguiente());
    }
}

De dos en dos … el siguiente valor es: 2 el siguiente valor es: 4 el siguiente valor es: 6 el siguiente valor es: 8 el siguiente valor es: 10 De tres en tres … el siguiente valor es: 3 el siguiente valor es: 6 el siguiente valor es: 9 el siguiente valor es: 12 el siguiente valor es: 15 Y por tanto debo crear una clase MasTres que produzca la salida anterior.

Ejercicio 2:

Tenemos una serie de clases muy diferentes que implementan el mismo interface Parlanchin según el siguiente diagrama UML: El método habla() genera una descripción textual del sonido del objeto que habla. Probamos la estructura desde Unidad4: UML ejercicio2

public class Unidad4 {
    public static void main(String[] args) {
        Gato g = new Gato();
        Perro p = new Perro();
        RelojCuco rc = new RelojCuco();
        g.habla();
        p.habla();
        rc.habla();
    }
}

que genera la salida

¡Miau! ¡Guau! ¡Cucu, cucu, ..!

todas las clases pertenecen al paquete parlanchines, y se permite que todas las clases hagan println().

Ejercicio3:

Ahora ampliamos el ejercicio anterior para incluir a Gato y Perro como subclase de la clase Abstracta Animal. UML ejercicio3