Está en la página 1de 15

UNIDAD III HERENCIA

¿QUÉ ES LA HERENCIA EN PROGRAMACIÓN ORIENTADA A OBJETOS?

Muchas veces distintos objetos comparten campos y métodos que hacen


aproximadamente lo mismo (por ejemplo almacenar y devolver un nombre del
ámbito humano con el que se designa al objeto, como el título de un álbum de
música, el título de un libro, el título de una película, etc.).

Por ejemplo en un proyecto que utilice objetos Taxi y objetos Autobus podríamos
encontrarnos algo así:

Para una aplicación de gestión de una empresa de transporte que tenga entre sus
vehículos taxis y autobuses podríamos tener otra clase denominada FlotaCirculante
donde tendríamos posibilidad de almacenar ambos tipos de objeto (por ejemplo
taxis en un ArrayList y autobuses en otro ArrayList) como reflejo de los vehículos
que se encuentran en circulación en una fecha dada. Esas listas conllevarían una
gestión para añadir o eliminar vehículos de la flota circulante, modificar datos, etc.
resultando que cada una de las listas necesitaría un tratamiento o mantenimiento.

Si nos fijamos en el planteamiento del problema, encontramos lo siguiente:

a) La definición de clases nos permite identificar campos y métodos que son


comunes a Taxis y Autobuses. Si implementamos ambas clases tal y como lo
venimos haciendo, incurriremos en duplicidad de código. Por ejemplo si el campo
matricula es en ambas clases un tipo String, el código para gestionar este campo
será idéntico en ambas clases.
b) La definición de clases nos permite identificar campos y métodos que
difieren entre una clase y otra. Por ejemplo en la clase Taxi se gestiona información
sobre un campo denominado numeroDeLicencia que no existe en la clase Autobus.

c) Conceptualmente podemos imaginar una abstracción que engloba a Taxis y


Autobuses: ambos podríamos englobarlos bajo la denominación de “Vehículos”. Un
Taxi sería un tipo de Vehiculo y un Autobus otro tipo de Vehiculo.

d) Si la empresa añade otros vehículos como minibuses, tranvías, etc. manteniendo


la definición de clases tal y como la veníamos viendo, seguiríamos engrosando
la duplicidad de código. Por ejemplo, un minibús también tendría matrícula,
potencia… y los métodos asociados.

La duplicidad de código nos implicará problemas de mantenimiento. Por ejemplo


inicialmente tenemos una potencia en caballos y posteriormente queremos definirla
en kilowatios. O tenemos simplemente que modificar el código de un método que
aparece en distintas clases. El tener el código duplicado nos obliga a tener que
hacer dos o más modificaciones en sitios distintos. Pueden ser dos modificaciones,
tres, cuatro o n modificaciones dependiendo del número de clases que se vieran
afectadas, y esto a la larga genera errores al no ser el mantenimiento razonable.

En la clase FlotaCirculante también tendremos seguramente duplicidades: por un


lado un ArrayList de taxis y por otro un ArrayList de autobuses, por un lado una
operación de adición de taxis y otra operación de adición de autobuses, por un lado
una operación para mostrar los elementos de la lista de taxis y otra para los
elementos de la lista de autobuses…

¿No sería más razonable, si una propiedad o método va a ser siempre común para
varios tipos de objetos, que estuviera localizada en un sitio único del que ambos
tipos de objeto “bebieran”? En los lenguajes con orientación a objetos la solución a
esta problemática se llama herencia. La herencia es precisamente uno de los puntos
clave de este tipo de lenguajes.

La herencia nos permite definir una clase como extensión de otra: de esta
manera decimos “la clase 1.1 tiene todas las características de la clase 1 y además
sus características particulares”. Todo lo que es común a ambas clases queda
comprendido en la clase “superior”, mientras lo que es específico, queda restringido
a las clases “inferiores”. En nuestro ejemplo definiríamos una clase denominada
Vehiculo, de forma que la clase Taxi tuviera todas las propiedades de la clase
Vehiculo, más algunas propiedades y métodos específicos. Lo mismo ocurriría con
la clase Autobus y otras que pudieran “heredar” de Vehiculo. Podríamos seguir
creando clases con herencia en un número indefinido: tantas como queramos. Si
piensas en el API de Java, hay cientos de clases que heredan de clases
jerárquicamente superiores como la clase Object. En un proyecto propio, podremos
tener varias clases que hereden de una clase común.
Esquema básico de herencia

Otro ejemplo: para la gestión de un centro educativo, podemos definir la clase


Persona que comprende los campos y métodos comunes a todas las personas.
Luego podremos definir la clase Estudiante, que es extensión de Persona, y
comprende los campos y métodos de la clase superior, más algunos específicos.
También podrían heredar de Persona la clase Profesor, la clase Director, la clase
JefeDeEstudios, la clase PersonalAdministrativo, etc.

La primera aproximación a la herencia en Java la plantearemos para plasmar todo


lo que hemos discutido en párrafos anteriores: en vez de definir cada clase por
separado con operaciones o campos duplicados en cierta medida, definiremos una
clase “padre” que contendrá todas las cosas que tengan en común otras clases.
Luego definiremos las otras clases “hijo” como extensión de la clase padre,
especificando para ellas únicamente aquello que tienen específico y distinto de la
clase padre. Una característica esencial de la herencia es que permite evitar la
duplicidad de código: aquello que es común a varias clases se escribe solo una
vez (en la clase padre). La duplicidad de información, ya sea código o datos, es algo
que por todos los medios hay que tratar de evitar en los sistemas informáticos por
ser fuente de errores y de problemas de mantenimiento.

Si nos remitimos al esquema básico de herencia donde hemos representado las


clases Vehiculo, Taxi y Autobus, la clase Vehiculo diremos que actúa como clase
padre de las clases Taxi y Autobus. Vehiculo recogería todo aquello que tienen o
hacen en común taxis y autobuses. Aunque aquello que tienen en común se agrupa,
tanto Taxi como Autobus tienen sus campos o métodos específicos.

Que una clase derive de otra en Java se indica mediante la palabra


clave “extends”. Por eso muchas veces se usa la expresión “esta clase es
extensión de aquella otra”. En los diagramas de clase la herencia se representa con
una flecha de punta vacía. El aspecto en BlueJ sería algo así:

Este diagrama refleja que la clase FlotaCirculante usa a la clase Vehiculo, mientras
que las clases Taxi y Autobus heredan de la clase Vehiculo (son extensión de la
clase Vehiculo). Podemos decir que la clase hijo extiende (hace más extensa) a la
clase padre. Una clase de la que derivan otras se denomina clase padre, clase base
o superclase. Las clases que heredan se denominan clases derivadas, clases hijos
o subclases. A partir de ahora los términos superclase y subclase son los que
usaremos con más frecuencia. Una subclase podrá tener un acceso “más o menos
directo” a los campos y métodos de la superclase en función de lo que defina el
programador, como veremos más adelante. Para referirnos a la herencia también
se usa la terminología “es un”. En concreto decimos que un objeto de la subclase
es un objeto de la superclase, o más bien en casos concretos, que un Taxi es un
Vehiculo, o que un Estudiante es una Persona. Sin embargo, al revés esto no es
cierto, es decir, un Vehiculo no es un Taxi, ni una Persona es un Estudiante.

Dos subclases que heredan de una clase no deben tener información duplicada.
Esto se consideraría un mal diseño. La información común debe estar en una
superclase.
EJERCICIO

Se plantea desarrollar un programa Java que permita la gestión de una empresa


agroalimentaria que trabaja con tres tipos de productos: productos frescos,
productos refrigerados y productos congelados. Todos los productos llevan alguna
información común como fecha de caducidad y número de lote, pero a su vez cada
tipo de producto lleva alguna información específica, por ejemplo los productos
congelados deben llevar la temperatura de congelación recomendada. Hay tres
tipos de productos congelados: congelados por aire, congelados por agua y
congelados por nitrógeno.

La empresa gestiona envíos a través de diferentes medios, y un envío puede


contener cierto número de productos frescos, refrigerados o congelados. Identificar
las 7 clases Java principales que podemos identificar dada la forma de
funcionamiento de la empresa. Crear un esquema con las relaciones de herencia
y/o uso entre las distintas clases.

JERARQUÍAS DE HERENCIA (CONCEPTO DE SUPERCLASE Y SUBCLASE)

JERARQUÍAS DE HERENCIA EN JAVA. SUPERCLASES Y SUBCLASES.

En Java muchas subclases pueden heredar de una misma superclase, y a su vez


una subclase puede convertirse en superclase de otra. Así las cosas, podemos
hablar de una jerarquía de herencia. La jerarquía es el esquema organizativo de las
clases con relación de herencia entre sí.

En un proyecto nosotros definimos nuestra propia jerarquía de herencia.


Las clases del API de Java igualmente tienen establecida una jerarquía de herencia,
en este caso definida por el equipo de desarrollo del lenguaje de la multinacional
Oracle.
La herencia es una técnica de abstracción que nos permite agrupar objetos
con características comunes. Todas las aplicaciones Java usan herencia,
aunque no lo hagan de forma explícita. Esto es así porque cuando creamos
una clase sin ninguna referencia a una superclase, el propio compilador
inserta en la clase creada una relación de herencia directa con la la clase
java.lang.Object. Por este motivo todos los objetos, sean de la clase que sean,
pueden invocar métodos de la clase java.lang.Object como equals() y
toString(), aunque no siempre se van a obtener buenos resultados así.

Si quisiéramos podríamos escribir para todas las clases public class


NombreDeLaClase extends Object, aunque como es algo implícito a Java
normalmente no lo escribiremos por ser redundante. En los diagramas
representativos de la jerarquía de herencia ocurre lo mismo: Object siempre está en
la cabecera del diagrama, aunque normalmente no se represente ya que se da por
sobreentendido. Recordar que en Java los tipos primitivos no son objetos: no son
instancias de clase, y por tanto no heredan de la superclase Object.
Los campos privados de una superclase no son accesibles por la subclase
directamente. Decimos que la subclase no tiene derechos de acceso sobre los
campos privados de la superclase. Para acceder a ellos tendrá que hacer uso de
métodos de acceso o modificación. Una subclase puede invocar a cualquier método
público de su superclase como si fuese propio. Lo veremos con ejemplos.

EJEMPLO DE HERENCIA EN JAVA. EXTENDS Y SUPER.

Para declarar la herencia en Java usamos la palabra clave extends. Ejemplo: public
class MiClase2 extends Miclase1. Para familiarizarte con la herencia te proponemos
que escribas y estudies un pequeño programa donde se hace uso de ella. Escribe
el código de las clases que mostramos a continuación.

//Código de la clase Persona ejemplo aprenderaprogramar.com


public class Persona {
private String nombre;
private String apellidos;
private int edad;
//Constructor
public Persona (String nombre, String apellidos, int edad) {
this.nombre = nombre;
this.apellidos = apellidos;
this.edad = edad; }
//Métodos
public String getNombre () { return nombre; }
public String getApellidos () { return apellidos; }
public int getEdad () { return edad; }
} //Cierre de la clase

//Código de la clase profesor, subclase de la clase Persona ejemplo aprenderaprogramar.com


public class Profesor extends Persona {
//Campos específicos de la subclase.
private String IdProfesor;
//Constructor de la subclase: incluimos como parámetros al menos los del constructor de la
superclase
public Profesor (String nombre, String apellidos, int edad) {
super(nombre, apellidos, edad);
IdProfesor = "Unknown"; } //Cierre del constructor
//Métodos específicos de la subclase
public void setIdProfesor (String IdProfesor) { this.IdProfesor = IdProfesor; }
public String getIdProfesor () { return IdProfesor; }
public void mostrarNombreApellidosYCarnet() {
// nombre = "Paco"; Si tratáramos de acceder directamente a un campo privado de la
superclase, salta un error
// Sí podemos acceder a variables de instancia a través de los métodos de acceso públicos
de la superclase
System.out.println ("Profesor de nombre: " + getNombre() + " " + getApellidos() +
" con Id de profesor: " + getIdProfesor() ); }
} //Cierre de la clase

//Código de test aprenderaprogramar.com


public class TestHerencia1 {
public static void main (String [ ] Args) {
Profesor profesor1 = new Profesor ("Juan", "Hernández García", 33);
profesor1.setIdProfesor("Prof 22-387-11");
profesor1.mostrarNombreApellidosYCarnet();}
} //Cierre de la clase

El diagrama de clases y el resultado del test son del tipo que mostramos a
continuación:
Profesor de nombre: Juan Hernández
García con Id de profesor: Prof 22-387-11

Los aspectos a destacar del código son:

a) La clase persona es una clase “normal” definida tal y como lo venimos haciendo
habitualmente mientras que la clase Profesor es una subclase de Persona con
ciertas peculiaridades.

b) Los objetos de la subclase van a tener campos nombre, apellidos y edad


(heredados de Persona) y un campo específico IdProfesor. El constructor de una
subclase ha de llevar obligatoriamente como parámetros al menos los mismos
parámetros que el constructor de la superclase.

c) El constructor de la subclase invoca al constructor de la superclase. Para


ello se incluye, obligatoriamente, la palabra clave super como primera línea del
constructor de la subclase. La palabra super irá seguida de paréntesis dentro de los
cuales pondremos los parámetros que requiera el constructor de la superclase al
que queramos invocar. En este caso solo teníamos un constructor de superclase
que requería tres parámetros. Si p.ej. hubiéramos tenido otro constructor que no
requiriera ningún parámetro podríamos haber usado uno u otro, es decir,
super(nombre, apellidos, edad) ó super(), o bien ambos teniendo dos constructores
para la superclase y dos constructores para la subclase. Ejemplo:

En la superclase: public Persona() {


nombre = "";
apellidos = "";
edad = 0; }
public Persona (String nombre, String apellidos, int
edad) {
this.nombre = nombre;
this.apellidos = apellidos;
this.edad = edad; }

En la subclase: public Profesor () {


super();
IdProfesor = "Unknown";}

public Profesor (String nombre, String apellidos, int


edad) {
super(nombre, apellidos, edad);
IdProfesor = "Unknown"; }

Modifica el código de las clases Persona y Profesor para que queden con dos
constructores tal y como hemos mostrado aquí. Crea objetos de ambos tipos en
BlueJ y prueba sus métodos.

¿Qué ocurre si olvidamos poner super como primera línea de la subclase? Hay
dos posibilidades: si la superclase tiene un constructor sin parámetros, el compilador
incluirá en segundo plano super de forma automática y no saltará un error. De
cualquier manera se considera contrario al buen estilo de programación, ya que no
queda claro si se trata de un olvido. Por ello incluiremos siempre la palabra clave
super. La otra posibilidad es que no haya un constructor sin parámetros, en cuyo
caso saltará un error.

A modo de resumen: la inicialización de un objeto de una subclase comprende dos


pasos. La invocación al constructor de la superclase (primera línea del constructor:
super…) y el resto de instrucciones propias del constructor de la subclase.

EJERCICIO

Se plantea desarrollar un programa Java que permita la gestión de una empresa


agroalimentaria que trabaja con tres tipos de productos: productos frescos,
productos refrigerados y productos congelados. Todos los productos llevan
esta información común: fecha de caducidad y número de lote. A su vez, cada tipo
de producto lleva alguna información específica. Los productos frescos deben
llevar la fecha de envasado y el país de origen. Los productos refrigerados deben
llevar el código del organismo de supervisión alimentaria. Los productos congelados
deben llevar la temperatura de congelación recomendada. Crear el código de las
clases Java implementando una relación de herencia desde la superclase
Producto hasta las subclases ProductoFresco, ProductoRefrigerado y
ProductoCongelado. Cada clase debe disponer de constructor y permitir establecer
(set) y recuperar (get) el valor de sus atributos y tener un método que permita
mostrar la información del objeto. Crear una clase testHerencia2 con el método main
donde se cree un objeto de cada tipo y se muestren los datos de cada uno de los
objetos creados.

EJERCICIO RESUELTO DE HERENCIA SIMPLE EN JAVA

En apartados anteriores del curso ya hemos comenzado con el estudio de la


herencia en programación orientada a objetos. La herencia puede comprender
numerosas clases, es decir, una clase puede heredar de otra clase que a su vez
herede de otra clase. Pensemos ahora que un Profesor hereda de Persona, y que
a su vez pueda ser ProfesorInterino o ProfesorTitular.

Trata de escribir el código de las clases ProfesorInterino y ProfesorTitular, de forma


que hereden de profesor y que a su vez cada una tenga sus propios métodos. Una
vez lo hayas hecho, escribe este código de ejemplo, la clase ProfesorInterino y una
clase de test TestHerencia2 y estudia cómo hemos desarrollado la herencia.

//Código de la clase ProfesorInterino ejemplo aprenderaprogramar.com


import java.util.Calendar;
public class ProfesorInterino extends Profesor {
private Calendar fechaComienzoInterinidad;
public ProfesorInterino(String nombre, String apellidos, int edad, Calendar
fechaInicioInterinidad) {
super(nombre, apellidos, edad);
fechaComienzoInterinidad = fechaInicioInterinidad;}
public Calendar getFechaComienzoInterinidad () { return fechaComienzoInterinidad;}
} //Cierre de la clase
import java.util.Calendar;
public class TestHerencia2 {
public static void main (String Args[]) {
Profesor profesor1 = new Profesor ("Juan", "Hernández García", 33);
profesor1.setIdProfesor("Prof 22-387-11");
profesor1.mostrarNombreApellidosYCarnet();
Calendar fecha1 = Calendar.getInstance();
fecha1.set(2019,10,22); //Los meses van de 0 a 11, luego 10 representa noviembre
ProfesorInterino interino1 = new ProfesorInterino("José Luis", "Morales Pérez", 54,
fecha1);
System.out.println("El profesor interino 1 se incorporó en la fecha: " +
fecha1.getTime().toString() );
}
} //Cierre de la clase ejemplo aprenderaprogramar.com

El diagrama de clases y el resultado del test son del tipo que mostramos a
continuación:

Profesor de nombre: Juan Hernández García


con Id de profesor: Prof 22-387-11
El profesor interino 1 se incorporó en la fecha:
Fri Nov 22 13:42:55 CET 2019
Los aspectos a destacar del código son:

a) Hemos usado la clase Calendar del API de Java. La importamos, declaramos


objetos de tipo Calendar y usamos algunos de sus métodos. No vamos a profundizar
en esta clase porque no es ese nuestro objetivo: en este código es sólo una clase
auxiliar para desarrollar este ejemplo.

b) La clase ProfesorInterino tiene dos constructores y ambos invocan a la


superclase Profesor mediante la palabra clave super. La superclase habrá de tener
también dos constructores, ya que en caso contrario saltará un error.

c) En el test creamos un ProfesorInterino pasando 4 parámetros al constructor: tres


de esos parámetros son gestionados por el constructor de la superclase, y el
restante es gestionado por el constructor de la subclase.

EJERCICIO

Se plantea desarrollar un programa Java que permita la gestión de una empresa


agroalimentaria que trabaja con tres tipos de productos: productos frescos,
productos refrigerados y productos congelados. Todos los productos llevan
esta información común: fecha de caducidad y número de lote. A su vez, cada tipo
de producto lleva alguna información específica. Los productos frescos deben
llevar la fecha de envasado y el país de origen. Los productos refrigerados deben
llevar el código del organismo de supervisión alimentaria, la fecha de envasado, la
temperatura de mantenimiento recomendada y el país de origen. Los productos
congelados deben llevar la fecha de envasado, el país de origen y la temperatura
de mantenimiento recomendada.

Hay tres tipos de productos congelados: congelados por aire, congelados por agua
y congelados por nitrógeno. Los productos congelados por aire deben llevar la
información de la composición del aire con que fue congelado (% de nitrógeno, %
de oxígeno, % de dióxido de carbono y % de vapor de agua). Los productos
congelados por agua deben llevar la información de la salinidad del agua con que
se realizó la congelación en gramos de sal por litro de agua. Los productos
congelados por nitrógeno deben llevar la información del método de congelación
empleado y del tiempo de exposición al nitrógeno expresada en segundos.

Crear el código de las clases Java implementando una relación de


herencia siguiendo estas indicaciones:
a) En primer lugar realizar un esquema con papel y bolígrafo donde se represente
cómo se van a organizar las clases cuando escribamos el código. Estudiar los
atributos de las clases y trasladar a la superclase todo atributo que pueda ser
trasladado.

b) Crear superclases intermedias (aunque no se correspondan con la descripción


dada de la empresa) para agrupar atributos y métodos cuando sea posible. Esto
corresponde a “realizar abstracciones” en el ámbito de la programación, que pueden
o no corresponderse con el mundo real.

c) Cada clase debe disponer de constructor y permitir establecer (set) y recuperar


(get) el valor de sus atributos y tener un método que permita mostrar la información
del objeto cuando sea procedente.

Crear una clase testHerencia3 con el método main donde se creen: dos productos
frescos, tres productos refrigerados y cinco productos congelados (2 de ellos
congelados por agua, otros 2 por aire y 1 por nitrógeno). Mostrar la información de
cada producto por pantalla.

También podría gustarte