gravatar

05 Curso de Java OO. Herencia y Polimorfismo

Resumen: Se estudiara el uso de la herencia y el polimorfismo en el reuso de clases.

I. HERENCIA

1.1 Definición
Permite a una clase compartir la misma estructura de datos y comportamiento de otra clase.
La herencia minimiza la necesidad de duplicar código.
El Polimorfismo permite utilizar el método de acuerdo al objeto heredado.

Superclase : Item
Subclase : Pelicula , Libro , Equipo (DVD, VHS)

Que atributos tienen en común las subclases?

Que atributos no tienen en común las subclases?

Que método no tienen en común las subclases? Defina las subclases Libro y Equipo.
Como declararía las clases:
Hombre > Homínido > Primate > Placentario
Perro > Canino > Carnivoro > Placentario
Placentario > Mamífero > Vertebrado > Animal > Ser Vivo.

1.2. La herencia en Java
Una subclase se define indicando a que superclase extiende.

public class Item {

// Definición de la superclase Item.
}
public class Pelicula extends Item {
// Atributos y métodos adicionales para distinguir una
// pelicula de otros tipos de item
}

Una subclase hereda todas las variables instancia de la superclase.
Las variables de instancia deben ser private para que instancias de la subclase hereden sus valores.

public class Item {
protected float precio = 0;
protected String estado = "Excelente";
}
public class Pelicula extends Item {
private String titulo = "";
private int duracion = 0;
}

1.3. La referencia super
Se refiere a la clase padre
Se usa para invocar constructores de la clase padre
Debe ser la primera sentencia del constructor de la clase hijo
Esta referencia también se usa para invocar cualquier método del padre.
public class Item {
protected float precio = 0;
Item (float precio) {
this.precio = precio;
}
}

public class Pelicula extends Item {
private String titulo = "";
Pelicula (float precio, String titulo) {
super(precio);
this.titulo = titulo;
}
}
Una subclase no hereda ningún constructor de la superclase, debe declararse explícitamente.
Solo en caso no se declare explícitamente, se ejecutaran los constructores por defecto de las superclases y finalmente de la subclase.

Pelicula pelicula = new Pelicula ();
// Inicia variables de la clase Item. Constructor por defecto.
// Inicia variables de la clase Pelicula. Constructor por defecto.

1.4. Métodos
La superclase define los métodos para todas las subclases.
La subclase puede especificar métodos propios.
Item0.java
public class Item0 {
protected float precio = 0;
Item0 (float precio) {
this.precio = precio;
}
public float getPrecio() {
return precio;
}
}
Pelicula0.java
public class Pelicula0 extends Item0
{
private String titulo = "";
Pelicula0 (float precio, String titulo) {
super(precio);
this.titulo = titulo;
}
public String getTitulo()
{
return titulo;
}
}

TestSuper.java
public class TestSuper {
public static void main (String[] args) {
Item0 item = new Item0(1.1f);
System.out.println( item.getPrecio() );
Pelicula0 pelicula = new Pelicula0(2.2f,"Zelig");
System.out.println( pelicula.getPrecio() );
System.out.println( pelicula.getTitulo() );
}
}

Que diferencia existe entre this y super?
Se puede reemplazar super(precio); por this.precio = precio; ?
Que métodos puede invocar una subclase?
La subclase hereda todos los métodos del padre.
La subclase puede re-escribir un método del padre.

Item1.java
public class Item1 {
public float calcularImporte(int cliente) {
return 50;
}
}
Pelicula1.java
public class Pelicula1 extends Item1 {
public float calcularImporte(int cliente) {
if (cliente < 500)
return 10;
else
return 30;
}
}

TestSobrescribir.java
public class TestSobrescribir {
public static void main (String[] args) {
Item1 item1 = new Item1();
System.out.println( item1.calcularImporte(599) )
Pelicula1 pelicula1 = new Pelicula1();
System.out.println( pelicula1.calcularImporte(399) );
System.out.println( pelicula1.calcularImporte(599) );
}
}

Cual es la diferencia entre sobre-carga de métodos y sobre-escritura de métodos?
1.5. La referencia super
Si una subclase sobrescribe un método de la superclase; el método de la superclase se puede invocar con la referencia super.
Item2.java
public class Item2 {
public float calcularImporte(int cliente) {
return 50;
}
}
Equipo2.java
public class Equipo2 extends Item2 {
public float calcularImporte(int cliente) {
float seguroEquipo = 25;
float alquiler = super.calcularImporte(cliente);
return seguroEquipo + alquiler;
}
}
TestSuper2.java
public class TestSuper2 {
public static void main (String[] args) {
Item2 articulo = new Item2();
System.out.println( articulo.calcularImporte(599) );
Equipo2 vhs = new Equipo2();
System.out.println( vhs.calcularImporte(599) );
}
}

2. Polimorfismo
Permite efectuar una misma operación dependiendo del tipo de objeto.
Ejemplo
TacoraFilms inicia sus operaciones alquilando únicamente películas.
Tres meses después amplia el alquiler a equipos, juegos y libros.
El alquiler de una pelicula es 2 soles por día de alquiler.
El alquiler de un equipo consta de un seguro de 50 soles además de 5 soles por día.
El alquiler de juegos depende del fabricante. PlayStation 2soles/día Nintendo 1sol/día
Los libros no se alquilan, se prestan uno a la vez, mientras sean clientes de la tienda.
Explique por que se obtienen los resultados
Alquiler3.java
public class Alquiler3 {
private int dias;
public Alquiler3(int dias) {
this.dias = dias;
}
public int getDias () {
return dias;
}
}
Item3.java
public class Item3 {
protected float calcularImporte(Alquiler3 contrato) {
return 0;
}
}
Pelicula3.java
public class Pelicula3 extends Item3 {
protected float calcularImporte(Alquiler3 contrato) {
int importe = 2*contrato.getDias();
return importe;
}
}
Equipo3.java
public class Equipo3 extends Item3 {
protected float calcularImporte(Alquiler3 contrato) {
int seguroEquipo = 50;
int importe = 5*contrato.getDias();
return seguroEquipo + importe;
}
}
Juego3.java
public class Juego3 extends Item3 {
String fabricante;
public Juego3(String fabricante) {
this.fabricante = fabricante;
}
public String getFabricante() {
return fabricante;
}
protected float calcularImporte(Alquiler3 contrato) {
String fabricante = this.fabricante;
int tasa = 0;
if (fabricante.equals("PlayStation")) tasa = 2;
if (fabricante.equals("Nintendo")) tasa = 1;
int importe = tasa*contrato.getDias();
return importe;
}
}
Libro3.java
public class Libro3 extends Item3 {
protected float calcularImporte(Alquiler3 contrato) {
return 0;
}
}
TestPolimorfismo.java
public class TestPolimorfismo {
public static void main (String[] args) {
Alquiler3 contrato = new Alquiler3(10);
Pelicula3 oscar = new Pelicula3();
System.out.println( oscar.calcularImporte(contrato) );
Equipo3 vhs = new Equipo3();
System.out.println( vhs.calcularImporte(contrato) );
Juego3 mu = new Juego3("Nintendo");
System.out.println( mu.calcularImporte(contrato) );
Libro3 quijote = new Libro3();
System.out.println( quijote.calcularImporte(contrato) );
}
}

Ejercicio
Agregue una nueva clase llamada Revista.java cuyo importe se basara en el día de la semana que se alquila.
Si es Sábado o Domingo el alquiler de las revistas será 5, en otro días será 2.

3. El operador instanceof y cast

El operador instanceof permite determinar la clase de un objeto en tiempo de ejecución.
La operación cast permite modificar la clase de un objeto.

El siguiente ejemplo muestra el uso de instanceOf y como el metodo is3D(Punto p) puede reconocerlo

Punto.java
import java.lang.Math;
public class Punto {
protected int x, y;
public Punto ( int x, int y ) {
this.x = x;
this.y = y;
}
// Distancia al eje de coordenadas
public float distancia() {
return Math.sqrt(Math.pow(x,2.0)+Math.pow(y,2.0));
}
}



Punto3D.java

import java.lang.Math;
public class Punto3D extends Punto {
private int z;
public Punto3D ( int x, int y, int z ) {
super(x,y);
this.z = z;
}
// Distancia al eje de coordenadas
public float distancia() {
return Math.sqrt(Math.pow(x,2.0)+Math.pow(y,2.0)+Math.pow(z,2.0));
}
}


TestInstanceOf.java

public class TestInstanceOf {
public static boolean is3D(Punto p) {
return (p instanceof Punto3D);
}
public static void main(String[] args) {
Punto p2 = new Punto(0,0);
Punto p3 = new Punto3D(0,0,10);
System.out.println(is3D(p2));
}
}

3.1. TestInstanceOfCast

El cast es implicito al convertir una clase hijo a una la clase padre.
El cast debe ser explicito al convertir de una clase padre a una clase hijo.

public class Movil {
String nombre;
Movil() { nombre = "Movil"; }
}
public class MovilPesado extends Movil {
MovilPesado() { nombre = "MovilPesado"; }
}
public class Camion extends MovilPesado {
Camion() { nombre = "Camion"; }
}
public class MovilLigero extends Movil {
MovilLigero() { nombre = "MovilLigero"; }
}
public class TestInstanceOfCast {
public static void main(String[] args) {
static boolean resultado;
static MovilPesado pesado = new MovilPesado();
static Camion volvo = new Camion();
static MovilPesado tren = null;
resultado = pesado instanceof MovilPesado;
System.out.print("pesado es un MovilPesado: " + resultado + "\n");
resultado = volvo instanceof MovilPesado;
System.out.print("volvo es un MovilPesado: " + resultado + "\n");
resultado = pesado instanceof Camion;
System.out.print("pesado is a Camion: " + resultado + "\n");
resultado = tren instanceof MovilPesado;
System.out.print("tren es un MovilPesado: " + resultado + "\n");
pesado = volvo; //Cast implicito. Un hijo se convierte en padre.
volvo = (Camion) pesado; //Cast explicito. Un padre se convierte en hijo.
}
}

3.2. TestOperador
El cast es implicito al convertir un hijo en padre. Revise aqui el paso de parametros al metodo testOperador.
Para convertir un padre a hijo, se usa cast explicito

public class TestOperador {
public static void main (String[] args) {
Pelicula3 oscar = new Pelicula3();
Equipo3 vhs = new Equipo3();
Juego3 mu = new Juego3("Nintendo");
Libro3 quijote = new Libro3();
testOperador(oscar);
testOperador(vhs);
testOperador(mu);
testOperador(agua);
}
public static void testOperador (Item3 articulo) {
if (articulo instanceof Juego3) {
Juego3 juego = (Juego3) articulo;
System.out.println(juego.getFabricante());
}
else {
System.out.println("No tiene Fabricante");
}
}
}

4. Atributos, métodos y clases final

4.1. Variables final
Una variable final es una constante
Una variable final no puede ser modificada
Una variable final debe ser iniciada
Una variable final por lo general es publica para que pueda ser accesada externamente.

Aqui no se puede cambiar los valores de las constantes

public class Constantes {
public final static String NEGRO = "FFFFFF";
public final static float PI = 3.141592f;
public final static int MAXIMO_ITEMS = 10;

Constantes() {
/*
NEGRO = '000000';
PI = 3.1416;
MAXIMO_ITEMS = 0;
*/
}
}

4.2. Métodos final Un método puede ser definida como final para evitar la sobre-escritura en una subclase.
Un método final no se puede redefinir en una clase hijo.

public final static String getBlanco() {
return "000000";
}
public final boolean verificarPassword(String password) {
if (password.equals(...
}

Aqui no se puede sobreescribir el metodo calcularImporte en la clase Pelicula4 por que calcularImporte de su clase padre es final.Item4.java
public class Item4 {
public final float calcularImporte(int cliente) {
return 50;
}
}
Pelicula4.java
public class Pelicula4 extends Item4 {
/* public float calcularImporte(int cliente) {
if (cliente < 500)
return 10;
else
return 30;
} */
}

4.3. Clases final

Una clase final no puede ser padre de otra clase.
Una clase puede ser definida como final para evitar la herencia.
El compilador es mas eficiente con definiciones final por que no buscara estas clases o métodos al tratar clases heredadas.

Aqui no se puede extender ninguna clase de Color.java

Color.java
public final class Color {
public final static String NEGRO = "FFFFFF";
public final static String getBlanco() {
return "000000";
}
}

/* Paleta.java
public final class Paleta.java extends Color.java {
...
}
*/

Ejercicio: Realizar lo siguiente.

Librería.java
public final class Libreria {
public final static String BLANCO = "000000";
public final static float PI = 3.141592f;

public final static int getModulo10(int numero)
{
// Obtiene el modulo 10 de la suma de digitos
if (numero < 0) numero = -numero;
String cadena = String.valueOf(numero);
int suma = 0;
for (int i=1; i < cadena.length(); i++) {
String caracter = cadena.substring(i-1,i);
int digito = Integer.parseInt(caracter);
suma += digito;
}
int residuo = suma%10;
return residuo;
}
}

TestLibreria.java
public class TestLibreria {
public static void main (String[] args) {
System.out.println(Libreria.BLANCO);
System.out.println(Libreria.PI);
System.out.println(Libreria.getModulo10(11));
System.out.println(Libreria.getModulo10(880385));
}
}

5. El método finalize()

Cuando todas las referencias de un objeto se pierden, se marcan para que el Garbage Collector los recoja y libere ese espacio en memoria.

Pelicula pelicula = new Pelicula("Zelig");
pelicula = null;

El objeto "Zelig" que estaba referenciado por pelicula ha perdido todas sus referencias.

Luego el Garbage Collector liberara el espacio ocupado por "Zelig"

El método finalize es llamado justo antes que el Garbage Collector libere la memoria. En este instante se puede aprovechar para realizar otras operaciones.

public class Pelicula4 {
private String titulo;
public Pelicula4(String titulo) {
this.titulo = titulo;
}
public void finalize()
{
System.out.println("Se acabo "+titulo);
}
}
public class TestFinalize {
public static void main (String[] args) {
Pelicula4 globo = new Pelicula4("Zelig");
globo = null;
}
}

6. Ejercicio
Encuentre los errores en el siguiente código.

Item.java
package alquiler;

public class Item {
public final int MAXIMO = 5;
public String getNombre() return "";
private String getColor() return "";
protected String getImporte() return "";
String getAlias() return "";
public final String getFabricante() return "";
}

Pelicula.java
package alquiler;
public class Pelicula extends Item {
public final int MAXIMO = 5;
public String getNombre() return super.getNombre();
private String getColor() return super.getColor();
protected String getImporte() return super.getImporte();
String getAlias() return super.getAlias();
public final String getFabricante()
return super.getFabricante();
}

Juguete.java
package almacen;
public final class Juguete {
Item item = new Item("");
public String getNombre() return item.getNombre();
public String getColor() return item.getColor();
protected String getImporte() return item.getImporte();
String getAlias() return item.getAlias();
}

Pelota.java
package almacen;
public final class Pelota extends Juguete {
public String getNombre() return super.getNombre();
public String getColor() return super.getColor();
protected String getImporte() return super.getImporte();
String getAlias() return super.getAlias();
}

7. Laboratorio.

Cree una clase Item.java con los siguientes atributos: id (int); titulo, descripción, ranking, categoría (String) , precio de alquiler por dia (double).
Todos estos atributos seran protected.
Agregue un atributo entero privado estático que inicia en 1000 para autogenerar el Id del item.
Agregue un constructor sin parámetros que autogenere un id para el item en base al atributo estático.
Agregue un segundo constructor con argumentos titulo, descripción, precio, ranking y categoría.
Este constructor invocara al primer constructor para asignar el id. Ademas asignara los argumentos a los atributos.
Agregue todos los métodos set y get para cada atributo.

Item.java
public class Item {
private static int siguienteId = 1000;
protected int id;
...
public Item() {
this.id = ++siguienteId;
}
public Item(String titulo... ) {
this();
this.titulo = titulo...
}
public int getId()...
...
public void setTitulo(String titulo)...
public String getTitulo()...
...
public static int getSiguienteId()...
}

La clase Cliente contara con 2 atributos, su id y su nombre. El id se generara automáticamente.
Cliente.java
public class Cliente {
private static int siguienteId = 2000;
protected int id;
...
public Cliente() {
this.id = ++siguienteId;
}
public Cliente(String nombre) {
this();
this.nombre...
}
public int getId()...
public String getNombre()...
}

Cree una clase Pelicula.java que herede de Item.java con los siguientes atributos: duracion (int) y director (String). Estos atributos seran privados.
Agregue un constructor con argumentos titulo, descripción, precio, ranking, numero de dias, categoría, duración y director.
Este constructor invocara al constructor de la superclase y luego asignara los dos ultimos argumentos a sus atributos privados.
Agregue todos los métodos set y get para sus dos atributos privados.
Agregue el método toString() que devuelva todos los atributos privados y heredados

Pelicula.java
public class Pelicula extends Item {
private String director;
private int duracion;
public Pelicula(String titulo ... int duracion) {
super(...);
this.director = director;
this.duracion = duracion;
}
public void setDirector...
public String getDirector...
public void setDuracion...
public String getDuracion...
public String toString...
}

Cree una clase Juego.java que herede de Item.java con los siguientes atributos: memoria (int) y tipo (String). Estos atributos seran privados.
Agregue un constructor con argumentos titulo, descripción, precio, ranking, numero de dias, categoría, memoria y tipo.
Este constructor invocara al constructor de la superclase y luego asignara los dos últimos argumentos a sus atributos privados.
Agregue todos los métodos set y get para sus dos atributos privados.
Agregue el método toString() que devuelva todos los atributos privados y heredados

Juego.java
public class Juego extends Item {
private int memoria;
private String tipo;
public Juego(String titulo ... String tipo) {
super(...);
this.memoria=...;
this.tipo=...;
}
...setMemoria...
...getMemoria...
...setTipo...
...getTipo...
public String toString...
}

En la base de datos se encontraran la lista de películas y los juegos.

BaseDeDatos.java
public class BaseDeDatos {

Cliente[] cliente = new Cliente [4];
Items[] item = new Item [4];

public BaseDeDatos() {

cliente[0] = new Cliente("Daneel Olivaw");
//...
item[0] = new Pelicula("La caravana del valor",...);
item[1] = new Pelicula("En las lunas de Endor",...);
item[2] = new Juego("Mu Online",...);
item[3] = new Juego("War Craft",...);
//...
}

public static Cliente getCliente(int id) {
Cliente cliente1 = null;
for (...) {
break;
}
return cliente1;
}

public static Item getItem(int id) ...
}

La clase Alquiler.java contara con los siguientes atributos: un arreglo de Ítems que se alquilaran (Item) el cliente que alquila (Cliente) un numero de alquiler id (int), la cantidad de ítems alquilados (int) y la cantidad de dias de alquiler (int).
Un atributo de clase privado estático entero será el contador de id que iniciara en 500.
Una constante será el máximo numero de ítems a alquilar que es 10.
Una constante será el impuesto del alquiler de 19%.
Un constructor asignara el cliente por argumento y el numero de dias de alquiler. También creara el arreglo de ítems, y su numero de alquiler.
Un método para agregar ítems al arreglo de ítems del alquiler.
Un método imprimira todo el detalle del alquiler en pantalla.
Alquiler.java
public class Alquiler {
private static int siguienteAlquilerId = 500;
private int alquilerId;
private int numeroDias;
private int clienteId;
private int cantidadItems;
private int[] itemId;
private final static int MAXITEMS = 10;
private final static double IMPUESTO = 0.19;
...
public Alquiler(int clienteId, int numeroDias) {
this.alquilerId = ...
this.clienteId = ...
...
itemId = new int[MAXITEMS];
}
public void agregarItem(int item) {
if (...) {
itemId[...] = item;
cantidadItems++;
}
else System.out.println("Demasiados ítems!");
}
public void imprimirReporte() {
/*
Cliente Id: 2000 Nombre: Salvor Hardin
Dias alquiler: 5
Detalle
1. El Padrino Precio = 1.10
2. Odisea 2001 Precio = 2.20
3. Aeropuerto 77 Precio = 3.30
Importe venta = 33.00
Importe total = 39.27
*/
}
}

El programa TacoraFilms realizara el alquiler
public class TacoraFilms {
public static void main(String[] args) {
Alquiler alquiler = new Alquiler(2001,5);
alquiler.agregarItem(1001);
alquiler.agregarItem(1002);
alquiler.agregarItem(1003);
alquiler.imprimerReporte();
}
}