Programación en castellano
-Tutoriales

Introducción a Java


Herencia (II)

Vamos a seguir estudiando la herencia en Java, que tiene cosas algo más avanzadas que las que vimos en el primer capítulo dedicado a ella.

. Clases y métodos abstractos

Como vimos anteriormente, es posible que con la herencia terminemos creando una familia de clases con un interfaz común. En esos casos es posible, y hasta probable, que la clase raíz de las demás no sea una clase útil, y que hasta deseemos que el usuario nunca haga instancias de ella, porque su utilidad es inexistente. No queremos implementar sus métodos, sólo declararlos para crear una interfaz común. Entonces declaramos sus métodos como abstractos:

public abstract void mi_metodo();

Como vemos, estamos declarando el método pero no implementandolo, ya que sustituimos el código que debería ir entre llaves por un punto y coma. Cuando existe un método abstracto deberemos declarar la clase abstracta o el compilador nos dará un error. Al declarar como abstracta una clase nos aseguramos de que el usuario no pueda crear instancias de ella:

Abstractos.java
abstract class Mamifero {
  String especie, color;
  public abstract void mover();
}

class Gato extends Mamifero {
  int numero_patas;
  public void mover() {
    System.out.println("El gato es el que se mueve");
  }
}

public class Abstractos {
  public static void main(String[] args) {
    Gato bisho = new Gato();
    bisho.mover();
  }
}

En nuestro ejemplo de herencia, parece absurdo pensar que vayamos a crear instancias de Mamifero, sino de alguna de sus clases derivadas. Por eso decidimos declararlo abstracto.

. Interfaces

Los interfaces tienen como misión en esta vida llevar el concepto de clase abstracta un poco más lejos, amén de permitirnos algo parecido a la herencia múltiple. Pero vamos pasito a pasito. Un interfaz es como una clase abstracta pero no permite que ninguno de sus métodos esté implementado. Es como una clase abstracta pero en estado más puro y cristalino. Se declaran sustituyendo class por interface:

interface Mamifero {
  String especie, color;
  public void mover();
}

class Gato implements Mamifero {
  int numero_patas;
  public void mover() {
    System.out.println("El gato es el que se mueve");
  }
}

No tenemos que poner ningún abstract en ningún sitio porque ya se le supone. Hay que fijarse también que ahora Gato no utiliza extends para hacer la herencia, sino implements.

Sin embargo, la mayor utilidad de los interfaces consiste en permitir la existencia de herencia múltiple, que consiste en que una clase sea heredera de más de una clase (que tenga varios papás, vamos). En C++ existía pero daba enormes problemas, al poder estar implementado un mismo método de distinta forma en cada una de las clases padre. En Java no existe ese problema. Sólo podemos heredar de una clase, pero podemos a su vez heredar de uno o varios interfaces (que no tienen implementación). Modifiquemos nuestro adorado ejemplo gatuno:

Interfaces.java
interface PuedeMoverse {
  public void mover();
}

interface PuedeNadar {
  public void nadar();
}

class Mamifero {
  String especie, color;
  public void mover() {
    System.out.println("El mamífero se mueve");
  }
}

class Gato extends Mamifero 
           implements PuedeMoverse, PuedeNadar {
  int numero_patas;
  public void mover() {
    System.out.println("El gato es el que se mueve");
  }
  public void nadar() {
    System.out.println("El gato nada");
  }
}

public class Interfaces {
  public static void main(String[] args) {
    Gato bisho = new Gato();
    bisho.mover();
    bisho.nadar();
  }
}

Vemos que Gato tiene la obligación de implementar los métodos mover() y nadar(), ya que sino lo hace provocará un error de compilación. Podría no implementar mover(), ya que hereda su implementación de Mamifero. Pero si decidiéramos no hacerlo no habría problemas, ya que tomaría la implementación de su clase padre, ya que los interfaces no tienen implementación. Así nos quitamos los problemas que traía la herencia múltiple de C++.

. Modificador final

Si declaramos un método como final, indicaremos que no puede ser sobreescrito en clases heredadas, por lo que mantendrá su implementación. Si es la clase la que lleva el modificador, entonces nos aseguramos de que nadie pueda heredar de dicha clase (la castramos, vaya). Por último, si declaramos una propiedad como final, crearemos una constante, ya que dicha variable se puede inicializar, pero no modificar. Este último caso presenta un uso más habitual de este modificador:

class Dias {
  static final int LUNES=0, MARTES=1, MIERCOLES=2, JUEVES=3, 
  VIERNES=4, SABADO=5, DOMINGO=6;
}

Como la clase es estática podremos acceder a las constantes como Dias.LUNES. Lo bueno del modificador final es que nos aseguramos que nadie pueda cambiar el valor de esas propiedades.

 
Patrocinados
 

Copyright © 1999-2007 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad

Hospedaje web y servidores dedicados linux por Ferca Network

red internet: musica mp3 | logos y melodias | hospedaje web linux | registro de dominios | servidores dedicados
más internet: comprar | recursos gratis | posicionamiento en buscadores | tienda virtual | gifs animados