Herencia

Una de las características de la programación orientada a objetos es la  herencia. El termino herencia es conocido por todos, quien no ha oído hablar de una herencia en la familia. Nosotros no vamos a heredar dinero ni propiedades pero si vamos a aprendera heredar recursos de otras clases.

Observemos el diseño de las siguientes clases:

La implementación en código sería la siguiente:

Como se puede apreciar en el gráfico, en UML utilizamos una flecha continua para representar la herencia.

La clase Empleado se conoce como superclase, clase base o también como clase madre, mientras que la clase Gerente recibe el nombre de subclase, clase derivada o clase hija.

La implementación de la clase Gerente utilizando herencia sería la siguiente:

Aunque sólo vemos la propiedad departamento declarado en la clase Gerente, de forma implícita tenemos todos los miembros heredados de la clase Empleado, nos referimos a las propiedades nombre, salario y fecha de nacimiento, así como al método getDetails().

En el lenguaje Java una clase solo puede heredar de otra, nunca de varias a la vez.

Esta restricción se denomina herencia sencilla o herencia simple.

Veamos un ejemplo más ampliado de herencia.

La clase Empleado contiene tres atributos (nombre, salario y fechaNacimiento), así como un método (getDetails). La clase Gerente hereda todos estos miembros y añade un atributo más, departamento, además de usar el método getDetails. La clase Director hereda todos los miembros de Empleado y Gerente, pero además agrega el atributo vehiculoEmpresa y un nuevo método, aumentarComision.

Asimismo, las clases Ingeniero y Secretario heredan los miembros de la clase

Empleado y podrían tener otros miembros específicos (no se muestran).

SOBREESCRITURA DE MÉTODOS

Además de poder agregar atributos para generar clases nuevas a partir de una clase existente, es posible modificar el comportamiento de la clase original. Si se define un método en una subclase de tal forma que su nombre, el tipo de retorno y la lista de argumentos son idénticos a los de la superclase (la clase de nivel superior), se dice que el nuevo método sobrescribe al antiguo.

Observe estos ejemplos de métodos en las clases Empleado y Gerente:

La clase Gerente contiene un método getDetails por definición, porque lo hereda de la clase Empleado. Pero el método original ha sido sustituido, sobrescrito, por la versión de la clase subordinada. Recuerde que, para que un método pueda sustituir al de una clase de nivel superior, su nombre y el orden de sus argumentos deben ser idénticos a los del método de la superclase. Asimismo, el método sustitutivo no puede ser menos accesible que el método al que sustituye.

REGLAS SOBREESCRITURA DE MÉTODOS

Si no cumplimos las reglas expuestas a continuación nos encontraremos con un error de compilación:

      • El tipo de dato devuelto en el método sobreescrito debe ser el mismo o una subclase del tipo de dato devuelto en el método de la superclase.
      • La lista de argumentos recibidos en el método sobreescrito debe ser la misma, nos referimos al mismo número, orden aunque pueden tener nombres diferentes.
      • No se puede hacer el método sobreescrito menos accesible.
      • Si el método original no tiene clausula throws no se la podemos poder. Lo que indica que si el método original no lanza excepciones, el sobreescrito tampoco.
      • Sin embargo, si el método original tiene clausula throws, el método sobreescrito debe lanzar las mismas excepciones o una subclase de ella. También podemos quitar la clausula throws en el método sobreescrito.

         

LLAMADA A METODOS SOBREESCRITOS

Un método de una subclase puede llamar a un método de una superclase utilizando la palabra clave super. La palabra super se refiere a la superclase de la clase en la que se ha utilizado esta clave. Se usa para hacer referencia a las variables miembro o a los métodos de la superclase. Se consigue utilizando la palabra clave super de la forma siguiente:

Una llamada con el formato super.metodo() llama al comportamiento completo, incluido cualquier efecto secundario del método al que se llamaría si el objeto hubiera sido una clase de nivel superior. El método no tiene por qué estar definido en la clase de nivel inmediatamente superior, puede heredarse de alguna clase situada más arriba en la jerarquía.

SOBRECARGA DE MÉTODOS

En algunos casos, puede que quiera escribir en la misma clase varios métodos que realizan la misma tarea básica pero con diferentes argumentos. Pensemos en un método sencillo destinado a enviar una representación textual de su argumento. Este método se podría llamar println(). Java y otros lenguajes de programación permiten reutilizar un mismo nombre de método para varios métodos. Esto sólo funciona si las circunstancias bajo las que se hace la llamada permiten distinguir cuál es el método necesario. En el caso de los tres métodos de impresión, esta distinción se basa en el número de argumentos y su tipo. Si se reutiliza el nombre de método, se obtienen los métodos siguientes:

Cuando se escribe el código para llamar a uno de estos métodos, se elige el método adecuado en función del tipo de argumento o argumentos que se suministran.

REGLAS SOBRECARGA DE MÉTODOS

Al igual que ocurre con la sobreescritura de métodos si no cumplimos unas determinadas reglas en la sobrecarga de métodos nos encontraremos con errores de compilación. Estas reglas son las siguientes:

        • Los métodos sobrecargados han de tener el mismo nombre. 
        • Pueden tener diferentes modificadores de acceso, lo que indica que uno puede ser público, otro privado, …etc. 
        • Pueden tener diferente numero de argumentos y en diferente orden.
        • El tipo devuelto puede ser diferente, pero cuidado. Si solo cambiamos esto genera un error de compilación porque el compilador piensa que estamos efectuando una sobreescritura.
        • No hay restricción en cuanto al lanzamiento de excepciones.
        • Los constructores no se heredan por lo que no se pueden sobreescribir, pero sí que puede haber sobrecarga.

 

SOBRECARGA DE CONSTRUCTORES

Cuando se instancia un objeto, el programa puede ser capaz de suministrar varios constructores basándose en los datos del objeto que se está creando. Por ejemplo, un sistema de nóminas podría ser capaz de crear un objeto Empleado si conoce todos los datos básicos sobre la persona: nombre, salario base y fecha de nacimiento. Puede que, en alguna ocasión, el sistema no conozca el salario base o la fecha de nacimiento. El siguiente ejemplo contiene cuatro constructores sobrecargados para la clase Empleado. El primer constructor inicializa todas las variables de instancia. En el segundo, no se incluye la fecha de nacimiento. La referencia this se utiliza como forma de envío de llamada a otro constructor (siempre dentro de la misma clase), en este caso, el primer constructor. Por su parte, el tercer constructor llama al primer constructor pasando la constante de clase SALARIO_BASE. El cuarto constructor llama al segundo constructor pasando SALARIO_BASE, que, a su vez, llama al primer constructor pasando null en lugar de la fecha de nacimiento.

La palabra clave this en un constructor debe ser la primera línea de código del constructor. Puede haber más código de inicialización después de la llamada this, pero no antes. Aunque las subclases heredan todos los métodos y variables de sus superclases, no heredan sus constructores. Las clases sólo pueden obtener un constructor de dos maneras: Debe escribirlo el programador o, si éste no lo escribe, debe usar el constructor predeterminado.

 
LLAMADA A UN CONSTRUCTOR DE LA SUPERCLASE
 
Al igual que los métodos, los constructores pueden llamar a los constructores no privados de la superclase inmediatamente anterior. A menudo se define un constructor con determinados argumentos y se quiere usar esos mismos argumentos para controlar la construcción de la parte superior de un objeto. Puede llamar a un determinado constructor de una superclase como parte de la inicialización de una subclase utilizando la palabra clave super en la primera línea del constructor de la subclase. Para controlar la llamada de ese constructor concreto, es preciso suministrar los argumentos adecuados a super(). Cuando no hay ninguna llamada a super con argumentos, se llama implícitamente al constructor de la superclase que tenga cero argumentos. En ese caso, si no hay ningún constructor de nivel superior que tenga cero argumentos, se produce un error de compilación. La llamada a super() puede adoptar cualquier cantidad de argumentos adecuados para los distintos constructores disponibles en la clase superior, pero debe ser la primera sentencia del constructor. Si recordamos que la clase Empleado tiene el conjunto de constructores definidos en el gráfico 46, entonces se definirían los siguientes constructores en Gerente.
 
El último constructor no es válido porque el compilador introduce una llamada implícita a la clase super() y en la clase Empleado no se ha suministrado ningún constructor sin argumentos. Si se utilizan, es preciso colocar super o this en la primera línea del constructor. Si escribe un constructor que no contiene ninguna llamada a super(…) ni a this(…), el compilador introduce una llamada al constructor de la superclase de forma automática y sin argumentos. Otros constructores pueden llamar también a super(…) o this(…), con lo que se hace una llamada a una cadena de constructores. Lo que ocurre al final es que el constructor de la clase de nivel superior (o quizás varios de ellos) se ejecuta antes que ningún otro constructor subordinado de la cadena.