Programación orientada a objetos: clases abstractas e interfaces

COMPARTIR 0 TWITTEAR

clases_abstractas_interface

Seguimos con nuestro curso de programación, en este artículo vamos a profundizar un poco con el tema de la reutilización de código. Al utilizar la herencia es muy común que al extender la funcionalidad de una clase la clase de la que extendemos, la superclase o clase padre, ya no tenga sentido instanciarla. En nuestro ejemplo de la granja recordamos que teníamos el tipo Animal y los subtipos Cerdo y Vaca que extendían de él.

Si nos fijamos es probable que en nuestra aplicación no volvamos a instanciar la clase Animal pues la funcionalidad ya la estamos generando cuando instanciamos las clases Vaca y Cerdo. Podemos ver todo esto desde un punto de vista diferente con un ejemplo.

Imaginemos que todos los animales de nuestra granja tengan que comer, tengan un método comer, el cual dependiendo del subtipo de Animal y su peso comerá más o menos. Entonces, ¿cómo implementamos este método?

Una primera opción sería declarar el método en la clase Animal, y que todas las demás clases hereden ese método y lo sobrescriban, pero ¿cómo lo implementamos? ¿vacío?

public void comer(){

}

No tiene mucho sentido hacer esto. Cuando utilizamos la herencia es muy probable que haya que declarar un método pero que la implementación de este método no tenga ningún sentido, este es el caso. Lo que realmente deberíamos hacer es mandar un mensaje desde la clase Animal a sus subclases y que estas se encarguen de implementarlo. Java resuelve este problema declarando el método como abstracto.

Métodos abstractos

En Java los métodos abstractos no tiene ningún tipo de implementación, para declararlos simplemente definimos una cabecera del método añadiendo la palabra reservada abstract. Vamos a llevar todo esto a nuestro ejemplo.

Empecemos declarando nuestro método abstracto en nuestra clase Animal:

public abstract void comer();

Si nos fijamos Eclipse nos avisará de dos fallos, aunque básicamente es uno: metodo_abstracto_error

Lo que nos dice es que o bien quitamos el método o bien definimos la clase Animal como abstract. Si vamos a las soluciones propuestas por Eclipse nos da dos opciones: solucion_metodo_abstract

Seleccionamos la segunda opción, hacer la clase Animal como abstracta. Una clase con ( al menos) un método abstracto, es una clase abstracta . Una clase abstracta es una clase cuyo propósito es servir de superclase para otras clases

  1. Ya sea para la generalización (polimorfismo)
  2. O para la reutilización de código, p.e. extensión (herencia)

Si nos fijamos ahora tendremos al menos estos tres fallos: problemas_clase_abstracta

  • Nos dice que no podemos instanciar la clase Animal.

Por ahora este problema no lo vamos a solucionar, lo dejaremos para más adelante. Ahora para que no nos salte el error vamos a la clase Granja y eliminamos el método leerConsola y todas las llamadas que se hagan de este método.

  • Debemos implementar el método comer tanto en la clase Vaca como la clase Cerdo. En todas aquellas subclases de la clase Animal.

Para solucionarlo vamos a la clase Vaca y la clase Cerdo y añadimos los siguientes métodos.

En la clase Cerdo añadimos:

@Override
public void comer() {
    System.out.println("El cerdo comerá: "+getPeso()*10+" kg de pienso.");
}   

En la clase Vaca añadimos:

@Override
 public void comer() {
    System.out.println("La vaca comerá: "+getPeso()*8+" kg de pienso.");
}

Si nos fijamos las clases abstractas obligan a las subclases a redefinir esos métodos declarados como abstractos. De lo contrario, las subclases serían también abstractas. En Java, es posible declarar una clase abstracta sin métodos abstractos, puesto que puede heredar los métodos abstractos de la superclase. Es probable que indique que se requiere la creación de subclases (una generalización).

Con esto ya tenemos una clase Animal que tiene implementado los métodos comunes que tendrán todas sus subclases y un mensaje (el método comer) que será implementado por las subclases. Pero … ¿y si queremos que nuestra clase Vaca herede de otra clase más? En Java solo podemos heredar de una clase (en C++ por ejemplo podemos de más) entonces, ¿cómo puedo enviar mensajes de una clase a otra? en Java esta función viene determinada por las interfaces.

Interfaces

Para compensar la falta de herencia múltiple, Java soporta el concepto de Interface. Una interfaz se define como el conjunto de mensajes que una clase ofrece a sus clientes (public). En Java, este concepto es ofrecido como un tipo. Por lo tanto, una interfaz Java es un conjunto de declaraciones de métodos, sin implementación. En Java tienen las siguientes características:

  • Las Interfaces pueden ser declaradas como public o no (package)
  • Todos sus métodos son abstract (no es necesario poner la palabra abstract)
  • Todos sus métodos son public (no es necesario poner public)
  • Las interfaces no tienen constructores.
  • Solo se permiten atributos con public static final.
  • Una interface puede heredar de otras interfaces.
  • Una clase puede heredar (implementar) múltiples interfaces.

Vamos a ver todo esto con un ejemplo. Supongamos que la clase Vaca debería implementar una interfaz llamada “Ordeñable”. Primero definimos la interface como:

public interface Ordeñable {
void ordeñar();
}

Añadimos el siguiente código en la clase Vaca

public class Vaca extends Animal implements Ordeñable {

public Vaca(String nombre) {
    super(nombre);
}

@Override
public void comer() {
    System.out.println("La vaca comerá: " + getPeso() * 8
            + " kg de pienso.");
}

@Override
public void ordeñar() {
    System.out.println("Estoy ordeñando la vaca: " + getNombre());
}

}

Si nos fijamos utilizamos la palabra reservada implements y redefinimos los métodos que tenga declarados la interfaz.

En resumen

A menudo se necesita la generalización múltiple. Dado que Java no proporciona herencia múltiple se utilizan las interfaces en su lugar. Destaquemos siempre que:

  • Una clase Java puede implementar varias interfaces.
  • La implementación de una interface no tiene nada que ver con la herencia (la falta de implementación se hereda debido a que cada método es abstracto).

Un uso común de las interfaces es para separar una funcionalidad de su aplicación ya que una interfaz define una funcionalidad (que debería ser único). Cada clase que implementa esta interfaz define las posibles implementaciones.

Finalicemos teniendo siempre en mente que:

  • Los métodos abstractos definen los mensajes que se pueden pasar a un objeto.
  • Las clases abstractas son superclases implementadas parcialmente.
  • La herencia múltiple no está soportada en Java, pero tenemos las interfaces que permiten generalizaciones múltiples.
  • Uso común de clases abstractas para reutilizar el código a través de la herencia.
  • Uso común de las interfaces para enviar mensajes que deben implementarse.

Una forma común de programación en Java es:

  1. Usar siempre interfaces para generalizar
  2. Usar solamente clases abstractas para la reutilización de la implementación (parcial) de la interfaz

Aquí tenéis el código fuente empleado.

Archivado en Curso de Programación, Java, Programación orientada a objetos
COMPARTIR 0 TWITTEAR

Comentarios (6)

Usa tu cuenta de Facebook para dejar tu opinión.

¿Te ha gustado? ¡No te pierdas nada más!

follow us in feedly

Otras webs de Difoosion