sábado, 16 de febrero de 2013

CONCEPTOS BÁSICOS DE JAVA (III)


HERENCIA
La herencia es un mecanismo que permite extender o reutilizar las características (atributos) y las acciones (métodos) que presenta un tipo de clase. Normalmente se utiliza para crear clases que reutilizan parte de la definición de otra clase. Por ejemplo si en mi programa tengo una clase Vehículo y quiero añadir una nueva clase Coche, que va a compartir mucha información (atributos) y acciones (métodos) con la clase Vehículo, es mejor reutilizar lo que me vale de Vehículo, en lugar que escribir desde cero la clase Coche. Entre otras muchas ventajas nos permite reducir el número de clases y líneas código de un programa en Java. Para indicar que una clase hereda de otra clase hay que utilizar la palabra extends en la definición de la clase hija. El ejemplo de Vehículo y Coche quedaría así:

public
class Vehiculo {
/* ATRIBUTOS */
// Atributos de instancia
private String matricula;
private String marca;
private String modelo;
private String tipoCombustible;
private int vehiculoNumero;
/* MÉTODOS */
// Constructor de clase
public Vehiculo(String matricula, String marca, String modelo,
String tipoCombustible)
{
this.matricula = matricula;
this.marca = marca;
this.modelo = modelo;
this.tipoCombustible = tipoCombustible;
vehiculoNumero = ++numeroVehiculo;
}

// Getter y setter
public String getMatricula() {
return matricula;
}
.
.
.
}
public class Coche extends Vehiculo {
private int numeroPuertas;
public Coche(String matricula, String marca, String modelo,
String tipoCombustible, int numeroPuertas)
{

/* Llamada al constructor del padre (Vehiculo), para inicializar
los atributos del padre (matricula, marca, modelo
y tipo de combustible). */
super(matricula, marca, modelo, tipoCombustible);
this.numeroPuertas = numeroPuertas;
}
public int getNumeroPuertas() {
return numeroPuertas;
}
public void setNumeroPuertas(int numeroPuertas) {
this.numeroPuertas = numeroPuertas;
}
.
.
.
}
En el ejemplo anterior a la clase padre (Vehículo) de la herencia se llama superclase y a la clase hija (Coche) se le llama subclase. En este punto tenemos que introducir un nuevo elemento, la referencia super. Este elemento se utiliza para acceder a los atributos y los métodos de la superclase (Vehículo) desde la subclase (Coche).
Al heredar lo que estamos haciendo realmente es encapsular la clase Vehículo dentro de la clase Coche, por este motivo todos los atributos y métodos de Vehículo estarán disponibles en la clase Coche. Es equivalente a construir una clase Coche con los siguientes atributos y métodos:
public class Coche {
/* ATRIBUTOS */
// Atributos de instancia
private String matricula;
private String marca;
private String modelo;
private String tipoCombustible;
private int vehiculoNumero;
private int numeroPuertas;
/* MÉTODOS */
// Constructor de clase
public Coche(String matricula, String marca, String modelo,
String tipoCombustible, int numeroPuertas)
{
this.matricula = matricula;
this.marca = marca;
this.modelo = modelo;
this.tipoCombustible = tipoCombustible;
this.numeroPuertas = numeroPuertas;
}
// Getter y setter
public String getMatricula() {
return matricula;
}
public void setMatricula(String matricula) {
this.matricula = matricula;
}
.
.
.
public int getNumeroPuertas() {
return numeroPuertas;
}
public void setNumeroPuertas(int numeroPuertas) {
this.numeroPuertas = numeroPuertas;
}
public String mostrarCaracteristicas() {
String caracteristicas = "Soy un vehiculo [matricula = " +
matricula + ", marca = " + marca + ",
modelo = " + modelo + ", combustible =
" + tipoCombustible + "] y soy de tipo
coche [numeroPuertas = " +
numeroPuertas + "]";
return caracteristicas;
}
}
Ahora imagine que también necesita crear nuevas clases que sean una especialización de Vehículo o de Coche: Moto, Mono-volumen, Deportivo, etc. Imagina la cantidad de código que tendría que repetir en cada una de estas clases. Como puede observar la herencia nos ahorra mucho trabajo a la hora de crear nuevas clases similares a clases de las que ya disponemos.


INTERFACES
Un interfaz es una colección de declaraciones de métodos (sin definir el código que se ejecutará en su interior). En esta declaración se indica el nombre del método, el valor devuelto por el método y los parámetros que recibe. Obliga a una clase concreta a definir, a la fuerza, una serie de métodos que son interesantes para que otra clase pueda utilizar esta clase con los métodos definidos en la interfaz. Es un mecanismo que asegura que varias clases puedan comunicarse entre sí. Permite definir un protocolo de comunicación entre clases. Por ejemplo imagina que queremos crear un juego de coches y nos interesa que cualquier coche de nuestro juego sea capaz de realizar las acciones de arrancar, parar, girar al centro, girar a la izquierda, girar a la derecha, acelerar, frenar y mover, entonces deberíamos crear la siguiente interfaz:
public interface IDesplazable {
// Métodos que nos interesa que cualquier Coche pueda realizar
void arrancar();
void parar();
void girarAlCentro();
void girarAIzquierda();
void girarADerecha();
void acelerar();
void frenar();
void mover();
}
Y la clase que implementa esta interfaz debería de indicarlo en su definición e implementar el contenido de estos métodos. Para indicar que una clase va a concretizar todos estos métodos, tenemos que utilizar la palabra implements en la declaración de la clase:
public class Coche implements IDesplazable {
private String marca;
.
.
.
@Override
public void arrancar() {
this.velocidad = 1;
}
@Override
public void parar() {
velocidad = 0;
}
@Override
public void girarAlCentro() {
this.orientacion = Orientacion.CENTRO;
}
@Override
public void girarAIzquierda() {
this.orientacion = Orientacion.IZQUIERDA;
}
@Override
public void girarADerecha() {
this.orientacion = Orientacion.DERECHA;
}
@Override
public void acelerar() {
velocidad++;
}
@Override
public void frenar() {
if (velocidad != 0)
velocidad--;
}
@Override
public void mover() {
switch (orientacion) {
case CENTRO:
posicionY += velocidad;
break;
case IZQUIERDA:
posicionX = posicionX - velocidad;
break;
case DERECHA:
posicionX += velocidad;
break;
default:
break;
}
}
La instrucción (anotación) @Override simplemente indica al compilador de Java que la clase Coche está dando una implementación concreta a cada uno de estos métodos, que en la interfaz están vacios. Se podría eliminar esta anotación y el código seguirá funcionando correctamente.