Herencia y polimorfismo

Índice
Herencia ... 2 Herencia: una subclase Alumno ... 6 Subclase Alumno2 con atributo de clase Persona ... 12 Los modificadores protected y final ... 14 Jerarquía de clases ... 15 Clases abstractas ... 18 Compatibilidad entre objetos de distintas clases ... 25 Polimorfismo y vinculación dinámica ... 27 Moldes de objetos y el operador instanceof ... 28 Interfaces ... 29 La interfaz Cloneable ... 31 Clases internas 35

Curso de Java

Herencia y polimorfismo
El lenguaje Java — Tema 6 — Herencia y polimorfismo

Curso de Java

Tema 5 – Herencia y polimorfismo - 1

Herencia
Mecanismo exclusivo y fundamental de la POO. La herencia no está contemplada en la programación basada en tipos (TAD). Es el principal mecanismo que ayuda a fomentar y facilitar la reutilización del software: Las clases como componentes software reutilizables. Si se necesita una nueva clase de objetos y se detectan suficientes similitudes con otra clase ya desarrollada, se toma esa clase existente como punto de partida para desarrollar la nueva: Se adoptan automáticamente características ya implementadas ⇒ Ahorro de tiempo y esfuerzo Se adoptan automáticamente características ya probadas ⇒ Menor tiempo de prueba y depuración

Herencia
Base de la aplicación del mecanismo de herencia: Suficientes similitudes: Todas las características de la clase existente (o la gran mayoría de ellas) resultan adecuadas para la nueva. En la nueva clase se ampliará y/o redefinirá el conjunto de características. Características de las clases que se adoptan: Todos los miembros definidos en las clases. Atributos Métodos Dependiendo de la forma en que se aplique el mecanismo de herencia, en la nueva clase se puede tener o no acceso a ciertas características heredadas (a pesar de que se adopten todas).
Curso de Java Tema 5 – Herencia y polimorfismo - 3

Curso de Java

Tema 5 – Herencia y polimorfismo - 2

Herencia
La relación de herencia se establece entre una nueva clase (referida aquí con el nombre Nueva) y una clase ya existente (referida aquí con el nombre Existente). Un poco de terminología: Existente se dice que es la clase base, la clase madre o la superclase (término genérico de la POO). Nueva se dice que es la clase derivada, la clase hija o la subclase (término genérico de la POO). También se utiliza el término derivación para referirse a la herencia. La clase Nueva es la que tiene establecida la relación de herencia con la clase Existente; Existente no necesita a Nueva, pero Nueva sí necesita la presencia de Existente.

Herencia
La relación de herencia se establece entre una clase Nueva y una clase Existente. Sobre la clase que hereda de la existente:
Nueva hereda todas las características de Existente. Nueva puede definir características adicionales. Nueva puede redefinir características heredadas de Existente. El proceso de herencia no afecta de ninguna forma a la superclase Existente.

Para que una nueva clase sea subclase de otra existente basta con añadir extends existente a continuación del nombre de la nueva:
public class Nueva extends Existente { ...
Curso de Java

Existente

Nueva

Curso de Java

Tema 5 – Herencia y polimorfismo - 4

Tema 5 – Herencia y polimorfismo - 5

Herencia: una subclase Alumno
Recordemos la clase Persona del tema anterior. Vamos a crear una subclase Alumno para representar alumnos de una clase. Los alumnos también tienen NIF, nombre, apellidos y edad: Los atributos de la clase Persona son adecuados para Alumno. Los métodos de la clase Persona, en principio, parecen todos ellos adecuados para la clase Alumno.

Herencia: una subclase Alumno
public class Alumno extends Persona { ... } Por el mero hecho de indicar que Alumno es subclase de Persona, los objetos de la clase Alumno ya tienen automáticamente 4 atributos y 12 métodos, los heredados de la clase Persona. En la subclase Alumno obviamente se puede acceder a lo público de la superclase Persona, pero NO se puede acceder a lo privado de la superclase Persona (los atributos en este caso). Ahora nos centramos en lo que hay que añadir en la subclase: Un atributo curso. Accedente y mutador para el nuevo atributo.

Los alumnos son personas
Resulta adecuado aplicar el mecanismo de herencia y crear la nueva clase (Alumno) a partir de la existente (Persona). public class Alumno extends Persona { ... }
Curso de Java Tema 5 – Herencia y polimorfismo - 6

Curso de Java

Tema 5 – Herencia y polimorfismo - 7

Herencia: una subclase Alumno
public class Alumno extends Persona { private int curso; // Accedente public int dameCurso() { return curso; } // Mutador public void ponCurso(int curso) { this.curso = curso; } }

Herencia: una subclase Alumno
public class Alumno extends Persona { private int curso; // Constructor predeterminado public Alumno() { super(); curso = 1; } // Constructor parametrizado public Alumno(long dni, int edad, String nombre, String apellidos, int curso) { super(dni, edad, nombre, apellidos); this.curso = curso; } public int dameCurso() { return curso; } public void ponCurso(int curso) { this.curso=curso; } }
Curso de Java Tema 5 – Herencia y polimorfismo - 9

La clase Alumno ya tiene un atributo y 2 métodos más (propios). En la subclase Alumno nos faltan los constructores. En los constructores podemos aprovechar lo que ya hacen los constructores de la superclase Persona, invocándolos por medio de la palabra reservada super.
Curso de Java Tema 5 – Herencia y polimorfismo - 8

Herencia: una subclase Alumno
Redefinición de métodos Algunos de los métodos heredados pueden no resultar adecuados (en mucho o en poco). Resulta necesario volver a desarrollarlos en la subclase. Para hacerlo basta crear un método en la subclase con el mismo nombre que el de la superclase y entonces se pasará por alto éste y se ejecutará el de la subclase. Pero si en un método redefinido se quiere ejecutar un método de la superclase que se ha redefinido, se puede utilizar la palabra clave super para pasarle el correspondiente mensaje.
public String toString() { // De la subclase Alumno return super.toString() + "Curso: " + curso + "\n"; }

Herencia: una subclase Alumno
public class Alumno extends Persona { private int curso; public Alumno() { super(); curso = 1; } public Alumno(long dni, int edad, String nombre, String apellidos, int curso) { super(dni, edad, nombre, apellidos); this.curso = curso; } public int dameCurso() { return curso; } public void ponCurso(int curso) { this.curso = curso; } public String toString() { return super.toString() + "Curso: " + curso + "\n"; } public void leer() { super.leer(); System.out.print("Curso: "); curso = MyInput.readInt(); } } Alumno.java
Curso de Java Tema 5 – Herencia y polimorfismo - 11

Curso de Java

Tema 5 – Herencia y polimorfismo - 10

Subclase Alumno2 con atributo de clase Persona
public class Alumno2 extends Persona { private int curso; private Persona profesor; public Alumno2() { super(); curso = 1; profesor = null; }
1

Subclase Alumno2 con atributo de clase Persona
public int dameCurso() { return curso; } public Persona dameProfesor() { return profesor; } public void ponCurso(int curso) { this.curso = curso; } public void ponProfesor(Persona profesor) { this.profesor = profesor; } public String toString() { String cadena = super.toString() + "Curso: " + curso + "\nProfesor: "; if(profesor == null) cadena += "no asignado.\n"; else cadena += profesor.nombreCompleto() + "\n"; return cadena; } public void leer() { super.leer(); System.out.print("Curso: "); curso = MyInput.readInt(); } }
Curso de Java

Persona

Alumno2

public Alumno2(long dni, int edad, String nombre, String apellidos, int curso, Persona profesor) { super(dni, edad, nombre, apellidos); this.curso = curso; this.profesor = profesor; } public Alumno2(long dni, int edad, String nombre, String apellidos, int curso) { super(dni, edad, nombre, apellidos); this.curso = curso; this.profesor = null; }
Curso de Java

. . .

Alumno2.java
Tema 5 – Herencia y polimorfismo - 13

Tema 5 – Herencia y polimorfismo - 12

Los modificadores protected y final
Si se quiere permitir que en las subclases se pueda acceder a los atributos de la superclase (o a métodos privados) se declararán éstos como protected en lugar de private.
public class Persona { protected Nif nif; protected int edad; protected String nombre, apellidos; ...

Jerarquía de clases
A medida que se establecen relaciones de herencia entre las clases, implícitamente se va construyendo una jerarquía de clases. En forma de árbol:
Object Persona

Y si se quiere evitar que se creen subclases de una clase se debe declarar como final.
public final class Persona { ... Oficial

Alumno

Administrativo Libre TiempoCompleto

Profesor TiempoParcial

Catedratico

Titular

Curso de Java

Tema 5 – Herencia y polimorfismo - 14

Curso de Java

Tema 5 – Herencia y polimorfismo - 15

Jerarquía de clases
Utilizando diagramas de UML (omitiendo la clase Object):
Persona

Jerarquía de clases
Cuanto más arriba en la jerarquía, más abstractas son las clases (menor nivel de detalle). A medida que se desciende por la jerarquía aumenta el nivel de detalle (disminuye la abstracción).
Oficial Persona Alumno Administrativo Profesor

Libre

TiempoCompleto

TiempoParcial

Alumno

Administrativo

Profesor

Así, una Persona es una entidad mucho más abstracta y general que un Titular.
Oficial Libre TiempoCompleto TiempoParcial

Catedratico

Titular

Cada clase de la jerarquía debe implementar todas las características que son comunes a todas sus subclases. Cada subclase debe contemplar únicamente las peculiaridades que la distinguen de su superclase.

Catedratico

Titular

Curso de Java

Tema 5 – Herencia y polimorfismo - 16

Curso de Java

Tema 5 – Herencia y polimorfismo - 17

Clases abstractas
En ocasiones tendremos definidas clases en la jerarquía que simplemente recogen las características comunes de otra serie de clases (sus descendientes), pero que no se van a (o no se deben) utilizar para crear ejemplares. Clase abstracta: Modela el comportamiento común de sus clases derivadas. Implementa métodos que son comunes a todas sus subclases. Establece métodos que necesariamente han de ser implementados por sus subclases (las clases derivadas). En el sistema no van a crearse ejemplares de la clase abstracta porque no serían objetos con existencia propia en el mundo real. Los objetos que se crearán serán ejemplares de las subclases (aquellas que no sean también abstractas). La clase abstracta puede definir atributos comunes a sus subclases.
Curso de Java Tema 5 – Herencia y polimorfismo - 18

Clases abstractas
ObjetoGrafico

Clases abstractas

Circulo

Punto

Paralelogramo

Elipse

Cuadrado

Rombo

ObjetoGrafico y Paralelogramo son clases abstractas.

Rectangulo

Romboide

En el programa de dibujo sólo se van a crear objetos gráficos concretos: puntos, elipses, círculos, cuadrados, rectángulos, rombos o romboides.

Curso de Java

Tema 5 – Herencia y polimorfismo - 19

Clases abstractas
Para definir una clase como abstracta se coloca la palabra reservada abstract antes de class:
public abstract class ObjetoGrafico { ...

Clases abstractas
// Clase Punto: no abstracta public class Punto extends ObjetoGrafico { private double x; private double y; public Punto() { x = 0; y = 0; } public Punto(double cx, double cy) { x = cx; y = cy; } public public public public
Curso de Java

Si en la clase abstracta se quiere obligar a que las subclases implementen un determinado método, basta declararlo como método abstracto. No tendrá cuerpo y terminará en punto y coma:
public abstract class ObjetoGrafico { // Abstracta public abstract String toString(); public abstract void leer(); public abstract boolean esCerrada(); }

Las subclases (no abstractas) no podrán compilarse si no implementan métodos con esos prototipos. ObjetoGrafico.java
Curso de Java Tema 5 – Herencia y polimorfismo - 20

double dameX() { double dameY() { void ponX(double void ponY(double

return return d) { x d) { y

x; } y; } = d; } = d; }

. . .

Tema 5 – Herencia y polimorfismo - 21

Clases abstractas
// Implementa todos los métodos abstractos: public void desplaza(double deltaX, double deltaY) { x += deltaX; y += deltaY; } public String toString(){ return "(" + x + "," + y + ")"; } public boolean esCerrada() { return false; } } }

Clases abstractas
// Paralelogramo: otra clase abstracta public abstract class Paralelogramo extends ObjetoGrafico { protected Punto esquina; public Paralelogramo() { esquina = new Punto(0, 0); } public Punto dameEsquina() { return esquina; } public void ponEsquina(Punto p) { esquina = p; } // Implementa uno de los métodos abstractos public boolean esCerrada() { return true; } // Todos los paralelogramos son cerrados.

Punto.java
Curso de Java Tema 5 – Herencia y polimorfismo - 22 Curso de Java

Paralelogramo.java
Tema 5 – Herencia y polimorfismo - 23

Clases abstractas
// Cuadrado: clase no abstracta public class Cuadrado extends Paralelogramo { protected double lado; public Cuadrado() { super(); lado = 0; } public double dameLado() { return lado; } public void ponLado(double d) { lado = d; } // Implementa los otros dos métodos abstractos public String toString() { return "[" + esquina.toString() + ", " + lado + "]"; } public void desplaza(double deltaX, double deltaY) { esquina.desplaza(deltaX, deltaY); } }
Curso de Java

Compatibilidad entre objetos de distintas clases
Todos los alumnos son personas, pero no todas las personas son alumnos
A un identificador de la clase Persona (superclase) se le puede asignar un objeto de la clase Alumno (subclase), pero a un identificador de la clase Alumno (subclase) no se le puede asignar un objeto de la clase Persona (superclase) Persona unaPersona; Alumno unAlumno; ... unaPersona = unAlumno; // Correcto: // todos los alumnos son personas unAlumno = unaPersona; // INCORRECTO: // no todas las personas son alumnos
Curso de Java Tema 5 – Herencia y polimorfismo - 25

Cuadrado.java
Tema 5 – Herencia y polimorfismo - 24

Compatibilidad entre objetos de distintas clases
Regla general de compatibilidad entre objetos: A un identificador de una clase sólo se le pueden asignar objetos de esa clase o de cualquiera de sus subclases Esta compatibilidad resulta muy útil cuando se quieren manejar listas de objetos: basta declarar un array de objetos de la superclase y podremos asignar a las distintas posiciones del array objetos de esa clase y de cualquiera de las subclases. La lista de Personas puede perfectamente guardar Alumnos. No hay que cambiar nada. Todo funciona sin cambios.

Polimorfismo y vinculación dinámica
En la clase Lista está definido el método toString():
public String toString() { String cad = "Elementos de la lista:\n\n"; for(int i = 0; i < _cont; i++) cad += _array[i].toString() + "\n"; return cad; }

Si en la lista se guardan tanto Personas como Alumnos, no podemos saber si cada _array[i] hace referencia a una Persona o a un Alumno. Y consecuentemente, no podemos saber si se ejecutará el método toString() de la clase Persona o el de la clase Alumno. _array[i] puede tener distintas formas: es polimórfico. La vinculación entre el mensaje y el método que corresponde se realiza en tiempo de ejecución: vinculación dinámica.

Curso de Java

Tema 5 – Herencia y polimorfismo - 26

Curso de Java

Tema 5 – Herencia y polimorfismo - 27

Moldes de objetos y el operador instanceof
Si queremos asignar a un identificador de una subclase el objeto referenciado por un identificador de una de sus superclases, deberemos utilizar un molde de objeto:
Persona unaPersona; ... Alumno unAlumno = (Persona) unaPersona;

Interfaces
Java no permite herencia múltiple (una clase extienda varias otras). Sin embargo, por medio de los interfaces se puede conseguir un efecto similar. Una interfaz es parecido a una clase abstracta, pero sólo puede tener definidos métodos abstractos y constantes (static/final). Ni atributos ni implementaciones de métodos. Se crear igual que las clases, pero utilizando interface en lugar de class:
public interface Comparable { // interfaz estándar public int compareTo(Object obj); // sin abstract }

Para que la asignación se pueda realizar, el objeto referenciado por unaPersona deberá ser en realidad un objeto Alumno. La regla de compatibilidad permite que unaPersona haga referencia a un objeto de clase Persona o a uno de clase Alumno. Deberemos asegurarnos de que se trate de un objeto Alumno. El operador instanceof indica si el objeto apuntado por una referencia es de una determinada clase:
if(unaPersona instanceof Alumno) unAlumno = (Persona) unaPersona;
Curso de Java Tema 5 – Herencia y polimorfismo - 28

Esta interfaz define un único método, que indica si el objeto receptor es menor que el proporcionado (resultado negativo), es mayor (resultado positivo) o es igual (0 como resultado). Recuérdese que Object es la clase raíz de la jerarquía.
Curso de Java Tema 5 – Herencia y polimorfismo - 29

Interfaces
Las clases pueden implementar interfaces, asegurando que incluyen la funcionalidad descrita en la interfaz (o las interfaces).
public class Persona2 implements Comparable { ... public int compareTo(Object obj) { String este = nombreCompleto().toUpperCase(); String otro = ((Persona) obj).nombreCompleto().toUpperCase(); return este.compareTo(otro); } Persona2.java }

La interfaz Cloneable
Para que Java permita crear clones de los objetos de una clase por medio del método clone() de la clase Object, la clase debe implementar la interfaz Cloneable.
public class Alumno3 extends Persona implements Cloneable { ... }

Además, la clase debe redefinir el método clone() heredado de la clase Object. Y debe hacerlo de esta forma:
public Object clone() { El método debe estar protegido con el try { manejo de esa excepción frente a intentos return super.clone(); de clonación de objetos de superclases } catch (CloneNotSupportedException ex) { return null; } Alumno3.java }
Curso de Java Tema 5 – Herencia y polimorfismo - 31

Nada impide que una clase herede de otra e implemente interfaces:
public class Alumno extends Persona implements Comparable { ...

Y se pueden implementar varias interfaces (separadas por comas).
Curso de Java Tema 5 – Herencia y polimorfismo - 30

La interfaz Cloneable
public class PruebaAlumno3 { public static void main(String args[]) { Alumno3 al1 = new Alumno3(435762, 23, "Javier", "Hernandez Perez", 4); Persona per = new Persona(112233, 22, "Pedro", "Gomez Alvarez"); al1.ponProfesor(per); System.out.println(al1); Alumno3 al2 = (Alumno3) al1.clone(); System.out.println(al2); al1.ponNombre("Angel"); per = al1.dameProfesor(); Shallow copy (copia superficial) per.ponNombre("Rosa"); El atributo profesor no se clona. System.out.println(al1); System.out.println(al2); } }

La interfaz Cloneable
Para conseguir una copia profunda (deep copy), el método clone() de la clase se debe encargar de obtener por su cuenta los clones de los atributos que sean referencias. En la clase Persona se debe permitir la clonación:
public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException ex) { return null; } }

PruebaAlumno3.java
Curso de Java Tema 5 – Herencia y polimorfismo - 32 Curso de Java

Persona3.java
Tema 5 – Herencia y polimorfismo - 33

La interfaz Cloneable
En la clase Alumno el método clone() debe crear el clon del profesor para colocarlo en su propio clon:
public Object clone() { Alumno4 al = (Alumno4) super.clone(); Persona3 per = (Persona3) profesor.clone(); al.ponProfesor(per); return al; }

Clases internas
Una clase interna o anidada es una clase que está definida dentro de otra clase (como si fuera otro atributo):
public class Clase { private int dato; public void metodo() { Interna ejemplar = new Interna(); }
Es una clase auxiliar class Interna { public void otro() { dato++; // Puede acceder a los atributos metodo(); // y a los métodos de la externa. } } Si la clase interna es anónima (sin nombre) se crea automáticamente un ejemplar suyo.

Como el método de la superclase Persona ya está protegido con el manejo de la excepción, aquí no hay que volver a protegerlo.

Alumno4.java PruebaAlumno4.java
Curso de Java Tema 5 – Herencia y polimorfismo - 34

}

La clase interna sólo es conocida en aquella en la que se encuentra.
Curso de Java Tema 5 – Herencia y polimorfismo - 35

Paquetes: empaquetado de clases
Son agrupaciones de clases, interfaces y otros paquetes relacionados entre sí, que favorecen la encapsulación. package nombrePaquete; Todos los elementos contenidos en el archivo con la declaración anterior formarán parte del paquete nombrePaquete. Podríamos crear un paquete con la información relativa a la matriculación de alumnos, que incluyera alumnos, asignaturas, notas, profesorado, horarios, etcétera. Posteriormente se podrían usar para diferentes aplicaciones de matriculación: institutos, colegios, ...
package Matricula; class Alumno { . . } class Asignatura { . . . } ...
Curso de Java Tema 5 – Herencia y polimorfismo - 36

Paquetes: empaquetado de clases
Para usar algún componente de un paquete hay que añadir una declaración de importación, que puede ser de un elemento o de todos los elementos. Un solo elemento:
import Matricula.Alumno; Se importa sólo la clase Alumno ... Alumno alumno1; ...

Todo el paquete sin cualificación:
import Matricula.*; ... Alumno alumno1; ... Se importa todo y para usar los elementos no es necesario cualificarlos

Curso de Java

Tema 5 – Herencia y polimorfismo - 37

Paquetes: ejemplo de empaquetamiento
Creo el directorio que va a contener clases (relacionadas):
cursojava utils teclado

Paquetes: ejemplo
Si quiero usar dichas clases en cualquier otro programa:
import cursojava.utils.teclado.*; class PruebaPaq { Clase01 c = new Clase02();

Carpeta/package con utilidades de lectura por teclado

Puedo usar todas las clases públicas del package teclado

Y en ese directorio creo dos archivos, cada uno con una clase útil:
package cursojava.utils.teclado; public class Clase01 {} package cursojava.utils.teclado; public class Clase02 {}

public static void main(String[] args){ Clase02 d = new Clase02()} }

A r vo Cl e01.ava chi as j A r vo Cl e02.ava chi as j

Cuando el compilador se encuentra import, comienza a buscar en los subdirectorio de CLASSPATH, y encuentra \utils\teclado, porque CLASSPATH= C:\.... Ahora puedo utilizar las clases públicas Clase01 y Clase02.
Curso de Java Tema 5 – Herencia y polimorfismo - 39

Al compilar dichos programa, se empaquetan dos clases .class en el directorio teclado.
Curso de Java Tema 5 – Herencia y polimorfismo - 38

Paquetes
Todas las clases se organizan en paquetes incluso las clases del sistema. Para utilizar algunas clases de un paquete o paquetes enteros se utiliza la instrucción import. Al importar un paquete sólo son accesibles sus elementos declarados como públicos, salvo para las clases que heredan de alguna de las declaradas en el paquete. Ejemplos de paquetes incluidos en el sistema:
java.lang: Clases esenciales de java: Object, String... java.util: Utilidades y estructuras de datos java.io: Todo lo relacionado con entrada/Salida java.net: Soporte para programación en red java.awt, java.swing: Soporte para interfaces gráficas

Paquetes

Curso de Java

Tema 5 – Herencia y polimorfismo - 40

Curso de Java

Tema 5 – Herencia y polimorfismo - 41

El modificador protected
package p1 cl s C1 as pr ect i x ot ed nt package p2 cl s C2 ext as ends C1 x s puede l o e eer m odii en C2 fcar

Ejercicio
Crear 5 clases vacías (Clase1, Clase2, Clase3, Clase 4 y Clase5) y empaquetarlas en C:/Prueba/Izar. Crear un programa PruebaPaquete en el proyecto Tema 6 que importe alguna clase para probarla

cl s C3 as C1 c1; c1. s puede l o x e eer m odii fcar

cl s C4 as C1 c1; c1. no s puede l x e eer o m odii fcar

¡Ojo!: JCreator crea las carpetas de los paquetes debajo de la carpeta del proyecto actual, por lo que habrá que cambiar el ClassPath para que encuentre dicho paquete

Curso de Java

Tema 5 – Herencia y polimorfismo - 42

Curso de Java

Tema 5 – Herencia y polimorfismo - 43