Herencia

video[Tutorial] La Herencia en Java

Uno de los grandes atractivos de la programación orientada a objetos es la facilidad que nos ofrece para la reutilización de código. La programación puede llegar a ser muy repetitiva. La reutilización de código consiste en no volver a escribir una y otra vez los mismos algoritmos para resolver situaciones similares. La clave para conseguir esto en Java va a ser la herencia. Si alguna vez ha tratado de reutilizar un código ya escrito para tratar de modificar su comportamiento, habrás comprobado lo complejo que resulta y cómo es muy frecuente cometer errores.

Veamos un ejemplo: Imagina  que dispones de un sistema de información geográfica donde cada punto geográfico es representado por la clase GeoPunto.  Los atributos de esta clase son longitud y latitud. Nos vemos obligados a cambiar el software dado que ahora necesitamos representar también la altitud de cada punto.

Una opción podría ser reescribir la clase GeoPunto y modificar todos sus métodos. Puede parecer sencillo, pero imagina que esta clase tuviera 10.000 líneas de código. El tiempo empleado y los errores cometidos serían frecuentes. Además todo el código que ya utilizaba GeoPunto tendría que ser modificado.

Java nos proporciona una forma mejor de trabajar. La idea consiste en definir una nueva clase, por ejemplo NuevoGeoPunto, que herede de GeoPunto y que añada el nuevo atributo. De esta forma podremos añadir nuevas funcionalidades sin tener que modificar la clase de partida. Más todavía, es posible que ni siquiera dispongamos del código de una clase para poder heredar de ella. En este apartado vamos a describir como la herencia nos permite resolver el problema de la reutilización de código de forma sencilla, pero a su vez eficiente.

Cuando queremos construir una nueva clase es muy frecuente que ya dispongamos de otra que realice parte de la que queremos hacer. La herencia consiste en crear la nueva clase a partir de otra que llamaremos padre. La clase hija va a heredar los atributos y métodos de la clase padre y podrá crear nuevos atributos y métodos para completar su comportamiento.

La clase hija también puede volver a definir los métodos de la clase padre. En tal caso es recomendable (no obligatorio) indicarlo con @Override; de esta forma evitamos errores habituales cuando cambiamos algún carácter o parámetro. Si un método ha sido sobrescrito podemos acceder al de la clase padre con el siguiente prefijo super.<método>(<parámetros>). Para llamar al constructor del padre super.(<parámetros>).

El constructor de la clase hija suele comenzar llamando al constructor de la clase padre, para ello escribiremos como primera línea de código: super.<método>(<parámetros>);

En el siguiente ejemplo se muestra una clase que hereda de Complejo. Esta nueva clase dispone del nuevo atributo esReal que nos indicará si este número pertenece a los números reales, es decir, la parte imaginaria es igual a cero:

La clase que acabamos de ver no tiene acceso a los atributos de la clase padre, dado que estos han sido declarados con private. Recuerda que si quieres dejar que las clases que hereden de ti tengan acceso a estos atributos, pero no así el resto de las clases, has de declararlos como protected. Dado que el acceso directo a los atributos es más eficiente que invocar los métodos getters y setters, puede resultar interesante declarar los atributos como protected. Aunque claro, en este caso la modificación de los atributos de la clase padre obligará a modificar todos los herederos que accedan directamente a estos atributos.

class ComplejoAmpliado extends Complejo {

  private boolean esReal;

  public ComplejoAmpliado(double re, double im) {
    super(re, im);
    esReal = im ==0;
  }

  public ComplejoAmpliado(double re) {
    super(re, 0);
    esReal= true;
  }

  @Override
  public void suma(Complejo v) {
    esReal = getImaginario() == - v.getImaginario();
    super.suma(v);
  }

  @Override
  public String toString() {
    if(esReal) {
      return getReal() + " ¡real!";
    } else{
      return super.toString();
    }
  }

  public boolean esReal(){
    return esReal;
  }

 }

Ejercicio paso a paso: La clase ComplejoAmpliado

1.     Dentro del  proyecto Complejos, crea la clase ComplejoAmpliado.

2.     Copia el código que aparece en el ejemplo anterior.

3.     Modifica la clase Principal para que en lugar de crear dos objetos como Complejo lo haga como  ComplejoAmpliado.. Verifica su funcionamiento.

4.     Modifica la suma para que el resultado sea un número real.

5.     Abre la clase Complejo y modifica la visibilidad de los atributos reemplazando private por protected.

6.     En la clase  reemplaza la línea:

   esReal = getImaginario() == - v.getImaginario();

por:

   esReal = imaginario == - v.imaginario;

7.     Verifica que no se ha introducido ningún error y que podemos ejecutar el proyecto.

Practica: Nueva clase coordenadas geográficas con altura

Crea la clase GeoPuntoAlt que herede de GeoPunto de forma que además de la longitud y latitud se almacena la altura de un punto geográfico. Para calcular la distancia teniendo en cuenta la diferencia de alturas, si suponemos que las distancias son cortas, podemos despreciar la curvatura de la tierra. De esta forma podemos aplicar Pitágoras para calcular la nueva distancia. Es decir: