3.1 Definición: Clase base y clase derivada
Introducción
La herencia es una propiedad esencial de la Programación Orientada a Objetos que consiste en la creación de nuevas clases a partir de otras ya existentes. Este término ha sido prestado de la Biología donde afirmamos que un niño tiene la cara de su padre, que ha heredado ciertas facetas físicas o del comportamiento de sus progenitores.
La herencia es la característica fundamental que distingue un lenguaje orientado a objetos, como el C++ o Java, de otro convencional como C, BASIC, etc. Java permite heredar a las clases características y conductas de una o varias clases denominadas base. Las clases que heredan de clases base se denominan derivadas, estas a su vez pueden ser clases bases para otras clases derivadas.
La herencia ofrece una ventaja importante, permite la reutilización del código. Una vez que una clase ha sido depurada y probada, el código fuente de dicha clase no necesita modificarse. Su funcionalidad se puede cambiar derivando una nueva clase que herede la funcionalidad de la clase base y le añada otros comportamientos. Reutilizando el código existente, el programador ahorra tiempo y dinero, ya que solamente tiene que verificar la nueva conducta que proporciona la clase derivada.
3.2 Clasificación: Herencia simple y Herencia múltiple
La herencia es un mecanismo que permite la definición de una clase a partir de la definición de otra ya existente. La herencia permite compartir automáticamente métodos y datos entre clases, subclases y objetos.
La herencia está fuertemente ligada a la reutilización del código en la POO. Esto es, el código de cualquiera de las clases puede ser utilizado sin más que crear una clase derivada de ella, o bien una subclase.
Hay dos tipos de herencia: Herencia Simple y Herencia Múltiple. La primera indica que se pueden definir nuevas clases solamente a partir de una clase inicial mientras que la segunda indica que se pueden definir nuevas clases a partir de dos o más clases iniciales. Java sólo permite herencia simple.
¿Existe la herencia múltiple en Java? ¿Por qué?
Objetivo: Fortalecer los conceptos de clases, métodos, propiedades o atributos, herencia, constructores y acerca de los métodos Get y Set. Todo esto con ayuda del programa realizado en la guía anterior con nuestro “Silabario para programadores”. El trabajo estructurado de la semana pasada ahora lo traslademos a una aplicación, siempre en el ambiente consola, en la cual definamos con un alto grado de encapsulamiento, las clases que harán posible la realización de la aplicación y llevarán definida todo el funcionamiento, o lo que es mismo, la capa de negocios del programa
Java es un lenguaje que incorpora herencia simple de implementación pero que puede aportar herencia múltiple de interfaz. Esto posibilita la herencia múltiple en el diseño de los programas Java.
Una interfaz puede heredar de más de una interfaz antecesora.
interface InterfazMultiple extends Interfaz1,Interfaz2{ }
Una clase no puede tener más que una clase antecesora, pero puede implementar más de una interfaz:
class Mi
Clase extends SuPadre implements Interfaz1,Interfaz2{ }
Herencia múltiple
El ejemplo típico de herencia múltiple es el que se presenta con la herencia en diamante:
Para poder llevar a cabo un esquema como el anterior en Java es necesario que las clases A, B y C de la figura sean interfaces, y que la clase D sea una clase (que recibe la herencia múltiple):
interface A{ }
interface B extends A{ }
interface C extends A{ }
class D implements B,C{ }
Colisiones en la herencia múltiple
En una herencia múltiple, los identificadores de algunos métodos o atributos pueden coincidir en la clase que hereda, si dos de las interfaces padres tienen algún método o atributo que coincida en nombre. A esto se le llama colisión.
Esto se dará cuando las clases padre (en el ejemplo anterior B y C) tienen un atributo o método que se llame igual. Java resuelve el problema estableciendo una serie de reglas.
Para la colisión de nombres de atributos, se obliga a especificar a qué interfaz base pertenecen al utilizarlos.
Para la colisión de nombres en métodos:
- Si tienen el mismo nombre y diferentes parámetros: se produce sobrecarga de métodos permitiendo que existan varias maneras de llamar al mismo.
- Si sólo cambia el valor devuelto: se da un error de compilación, indicando que no se pueden implementar los dos.
- Si coinciden en su declaración: se elimina uno de los dos, con lo que sólo queda uno.
3.3 Reutilización de miembros heredados
La reutilización de código se refiere al comportamiento y a las técnicas que garantizan que una parte o la totalidad de un programa informático existente se pueda emplear en la construcción de otro programa. De esta forma se aprovecha el trabajo anterior, se economiza tiempo, y se reduce la redundancia.
La manera más fácil de reutilizar código es copiarlo total o parcialmente desde el programa antiguo al programa en desarrollo. Pero es trabajoso mantener múltiples copias del mismo código, por lo que en general se elimina la redundancia dejando el código reusable en un único lugar, y llamándolo desde los diferentes programas. Este proceso se conoce como abstracción. La abstracción puede verse claramente en las bibliotecas de software, en las que se agrupan varias operaciones comunes a cierto dominio para facilitar el desarrollo de programas nuevos. Hay bibliotecas para convertir información entre diferentes formatos conocidos, acceder a dispositivos de almacenamiento externos, proporcionar una interfaz con otros programas, manipular información de manera conocida (como números, fechas, o cadenas de texto).
Para que el código existente se pueda reutilizar, debe definir alguna forma de comunicación o interfaz. Esto se puede dar por llamadas a una subrutina, a un objeto, o a una clase.
Como se ha comentado anteriormente la clase descendiente puede añadir sus propios atributos y métodos pero también puede sustituir u ocultar los heredados. En concreto:
1. Se puede declarar un nuevo atributo con el mismo identificador que uno heredado, quedando este atributo oculto. Esta técnica no es recomendable.
2. Se puede declarar un nuevo método de instancia con la misma cabecera que el de la clase ascendiente, lo que supone su sobreescritura. Por lo tanto, la sobreescritura o redefinición consiste en que métodos adicionales declarados en la clase descendiente con el mismo nombre, tipo de dato devuelto y número y tipo de parámetros sustituyen a los heredados.
3. Se puede declarar un nuevo método de clase con la misma cabecera que el de la clase ascendiente, lo que hace que éste quede oculto. Por lo tanto, los métodos de clase o estáticos (declarados como static) no pueden ser redefinidos.
4. Un método declarado con el modificador final tampoco puede ser redefinido por una clase derivada.
5. Se puede declarar un constructor de la subclase que llame al de la superclase de forma implícita o de mediante la palabra reservada super.
6. En general puede accederse a los métodos de la clase ascendiente que han sido redefinidos empleando la palabra reservada super delante del identificador del método. Este mecanismo sólo permite acceder al metodo perteneciente a la clase en el nivel inmediatamente superior de la jerarquía de clases.
3.4 Referencia al objeto de la clase base
Consideremos las figuras planas cerradas como el rectángulo, y el círculo. Tales figuras comparten características comunes como es la posición de la figura, de su centro, y el área de la figura, aunque el procedimiento para calcular dicha área sea completamente distinto. Podemos por tanto, diseñar una jerarquía de clases, tal que la clase base denominada Figura, tenga las características comunes y cada clase derivada las específicas. La relación jerárquica se muestra en la figura

La clase Figura es la que contiene las características comunes a dichas figuras concretas por tanto, no tiene forma ni tiene área. Esto lo expresamos declarando Figura como una clase abstracta, declarando la función miembro area abstract.
Las clases abstractas solamente se pueden usar como clases base para otras clases. No se pueden crear objetos pertenecientes a una clase abstracta. Sin embargo, se pueden declarar variables de dichas clases.
En el juego del ajedrez podemos definir una clase base denominada Pieza, con las características comunes a todas las piezas, como es su posición en el tablero, y derivar de ella las características específicas de cada pieza particular. Así pues, la clase Pieza será una clase abstracta con una función abstract denominada mover, y cada tipo de pieza definirá dicha función de acuerdo a las reglas de su movimiento sobre el tablero.
3.5 Constructores y destructores en clases derivadas
Al instanciar objetos de clases derivadas se inicia una cadena de invocaciones a constructores en las cuales el constructor de la clase derivada, antes de realizar sus propias tareas, invoca (ya sea implícita o explícitamente) al constructor de su clase base. Similarmente, si la clase base fue derivada de otra clase, el constructor de la clase base debe invocar al constructor de la clase ubicada en el siguiente nivel superior de la jerarquía, y así sucesivamente. El último constructor invocado en la cadena es el constructor de la clase Object, cuyo cuerpo se ejecuta primero. El cuerpo del constructor de la clase derivada se ejecuta al final. El constructor de cada clase base inicializa las variables de instancia que el objeto de la clase derivada hereda.
Destructores en clases derivadas
Cuando remueve de la memoria un objeto de una clase derivada, el recolector de basura invoca al destructor del objeto. Esto inicia una cadena de invocaciones a destructores, en donde el destructor de la clase derivada y los destructores de las clases bases directas e indirectas se ejecutan en orden inverso al que se ejecutaron los constructores, esto es, primero se ejecuta el destructor de la clase derivada y al final se ejecuta el destructor de la clase base ubicada en el nivel superior de la jerarquía. La ejecución de los destructores debe liberar todos los recursos que el objeto adquirió, antes de que el recolector de basura reclame la memoria de ese objeto. Cuando el recolector de basura invoca al destructor de un objeto de una clase derivada, ese destructor realiza su tarea y después invoca al destructor de la clase base. El proceso se repite hasta que se invoca al destructor de la clase Object.
3.6 Redefinicion de Metodos en clases derivadas
El lenguaje java permite redefinir miembros de la clase base en las clases derivadas, pero el compilador emite una advertencia cuando detecta una redefinición. Una advertencia (warning) es un mensaje del compilador acerca de un posible problema. Sin embargo, en este caso sí se genera código ejecutable (a diferencia del mensaje de error). Redefinición de campos. El siguiente ejemplo muestra cómo reutilizar los identificadores de los campos de la clase base en una clase derivada.
Ejemplo de redefinición de las clases derivadas
// Redef.cs : Ejemplifica la redefinición de campos en clases derivadas.
class Punto{
public int x;
public int y;
}
class Punto3D : Punto{
public int x ;
public int y ;
public int z ;
}
class Principal{
public static void Main( ) {
Punto a = new Punto( ); Punto3D b = new Punto3D( );
a.x = 100 ;
a.y = 200 ;
b.x = 300 ;
b.y = 400 ;
b.z = 500 ;
}
}