Capitulo 10
Capitulo 10
Capitulo 10
Polimorfismo
10.1 Introduccin
El polimorfismo nos permite escribir programas para procesar objetos que compartan la misma superclase en una jerarqua de clases, como si todos fueran objetos de la superclase; esto puede simplificar la programacin. Con el polimorfismo, podemos disear e implementar sistemas que puedan extenderse con facilidad; pueden agregarse nuevas clases con slo modificar un poco (o nada) las porciones generales del programa, siempre y cuando las nuevas clases sean parte de la jerarqua de herencia que el programa procesa en forma genrica. Las nicas partes de un programa que deben alterarse para dar cabida a las nuevas clases son las que requieren un conocimiento directo de las nuevas clases que el programador agregar a la jerarqua.
// Fig. 10.1: PruebaPolimorfismo.java // Asignacin de referencias a la superclase y la subclase, a variables de la superclase y la subclase. public class PruebaPolimorfismo { public static void main( String args[] ) { // asigna la referencia a la superclase a una variable de la superclase EmpleadoPorComision3 empleadoPorComision = new EmpleadoPorComision3( "Sue", "Jones", "222-22-2222", 10000, .06 ); // asigna la referncia a la subclase a una variable de la subclase EmpleadoBaseMasComision4 empleadoBaseMasComision = new EmpleadoBaseMasComision4( "Bob", "Lewis", "333-33-3333", 5000, .04, 300 ); // invoca a toString en un objeto de la superclase, usando una variable de la superclase System.out.printf( "%s %s:\n\n%s\n\n", "Llamada a toString de EmpleadoPorComision3 con referencia de superclase ", "a un objeto de la superclase", empleadoPorComision.toString() ); // invoca a toString en un objeto de la subclase, usando una variable de la subclase System.out.printf( "%s %s:\n\n%s\n\n", "Llamada a toString de EmpleadoBaseMasComision4 con referencia", "de subclase a un objeto de la subclase", empleadoBaseMasComision.toString() ); // invoca a toString en un objeto de la subclase, usando una variable de la superclase EmpleadoPorComision3 empleadoPorComision2 = empleadoBaseMasComision; System.out.printf( "%s %s:\n\n%s\n", "Llamada a toString de EmpleadoBaseMasComision4 con referencia de superclase", "a un objeto de la subclase", empleadoPorComision2.toString() ); } // fin de main } // fin de la clase PruebaPolimorfismo
Llamada a toString de EmpleadoPorComision3 con referencia de superclase a un objeto de la superclase: empleado por comision: Sue Jones numero de seguro social: 222-22-2222 ventas brutas: 10000.00 tarifa de comision: 0.06
El operador instanceof se puede utilizar para determinar si el tipo de un objeto especfico tiene la relacin es un con un tipo especfico. Todos los objetos en Java conocen su propia clase y pueden acceder a esta informacin a travs del mtodo getClass, que todas las clases heredan de la clase Object. El mtodo getClass devuelve un objeto de tipo Class (del paquete java.lang), el cual contiene informacin acerca del tipo del objeto, incluyendo el nombre de su clase.
La relacin es un se aplica slo entre la subclase y sus superclases, no viceversa. No est permitido tratar de invocar a los mtodos que slo pertenecen a la subclase, en una referencia a la superclase. Aunque el mtodo que se llame en realidad depende del tipo del objeto en tiempo de ejecucin, podemos usar una variable para invocar slo a los mtodos que sean miembros del tipo de esa variable, que el compilador verifica.
// Fig. 10.4: Empleado.java La superclase abstracta Empleado. public abstract class Empleado { private String primerNombre; private String apellidoPaterno; private String numeroSeguroSocial; // constructor con tres argumentos public Empleado( String nombre, String apellido, String nss ) { primerNombre = nombre; apellidoPaterno = apellido; numeroSeguroSocial = nss; } // fin del constructor de Empleado con tres argumentos // establece el primer nombre public void establecerPrimerNombre( String nombre ) { primerNombre = nombre; } // fin del mtodo establecerPrimerNombre // devuelve el primer nombre public String obtenerPrimerNombre() { return primerNombre; } // fin del mtodo obtenerPrimerNombre // establece el apellido paterno public void establecerApellidoPaterno( String apellido ) { apellidoPaterno = apellido; } // fin del mtodo establecerApellidoPaterno // devuelve el apellido paterno public String obtenerApellidoPaterno() { return apellidoPaterno; } // fin del mtodo obtenerApellidoPaterno // establece el nmero de seguro social public void establecerNumeroSeguroSocial( String nss ) { numeroSeguroSocial = nss; // debe validar } // fin del mtodo establecerNumeroSeguroSocial // devuelve el nmero de seguro social public String obtenerNumeroSeguroSocial() { return numeroSeguroSocial; } // fin del mtodo obtenerNumeroSeguroSocial // devuelve representacin String de un objeto Empleado public String toString() { return String.format( "%s %s\nnumero de seguro social: %s", obtenerPrimerNombre(), obtenerApellidoPaterno(), obtenerNumeroSeguroSocial() ); } // fin del mtodo toString // mtodo abstracto sobrescrito por las subclases public double ingresos(); // extends aqu no hay implementacin publicabstract class EmpleadoAsalariado Empleado {
private salarioSemanal; } // fin de la double clase abstracta Empleado // constructor de cuatro argumentos public EmpleadoAsalariado( String nombre, String apellido, String nss, double salario ) { super( nombre, apellido, nss ); // los pasa al constructor de Empleado establecerSalarioSemanal( salario ); // valida y almacena el salario } // fin del constructor de EmpleadoAsalariado con cuatro argumentos // establece el salario
// Fig. 10.6: EmpleadoPorHoras.java La clase EmpleadoPorHoras extiende a Empleado. public class EmpleadoPorHoras extends Empleado { private double sueldo; // sueldo por hora private double horas; // horas trabajadas por semana // constructor con cinco argumentos public EmpleadoPorHoras( String nombre, String apellido, String nss, double sueldoPorHoras, double horasTrabajadas ) { super( nombre, apellido, nss ); establecerSueldo( sueldoPorHoras ); // valida y almacena el sueldo por horas establecerHoras( horasTrabajadas ); // valida y almacena las horas trabajadas } // fin del constructor de EmpleadoPorHoras con cinco argumentos // establece el sueldo public void establecerSueldo( double sueldoPorHoras ) { sueldo = ( sueldoPorHoras < 0.0 ) ? 0.0 : sueldoPorHoras; } // fin del mtodo establecerSueldo // devuelve el sueldo public double obtenerSueldo() { return sueldo; } // fin del mtodo obtenerSueldo // establece las horas trabajadas public void establecerHoras( double horasTrabajadas ) { horas = ( ( horasTrabajadas >= 0.0 ) && ( horasTrabajadas <= 168.0 ) ) ? horasTrabajadas : 0.0; } // fin del mtodo establecerHoras
// devuelve las horas trabajadas public double obtenerHoras() { return horas; } // fin del mtodo obtenerHoras // calcula los ingresos; sobrescribe el mtodo abstracto ingresos en Empleado public double ingresos() { if ( obtenerHoras() <= 40 ) // no hay tiempo extra return obtenerSueldo() * obtenerHoras(); else return 40 * obtenerSueldo() + ( obtenerHoras() - 40 ) * obtenerSueldo() * 1.5; } // fin del mtodo ingresos // devuelve representacin String de un objeto EmpleadoPorHoras public String toString() { return String.format( "empleado por horas: %s\n%s: $%,.2f; %s: %,.2f", super.toString(), "sueldo por hora", obtenerSueldo(), "horas trabajadas", obtenerHoras() ); } // fin del mtodo toString } // fin de la clase EmpleadoPorHoras
// Fig. 10.7: EmpleadoPorComision.java La clase EmpleadoPorComision extiende a Empleado. public class EmpleadoPorComision extends Empleado { private double ventasBrutas; // ventas totales por semana private double tarifaComision; // porcentaje de comisin // constructor con cinco argumentos public EmpleadoPorComision( String nombre, String apellido, String nss, double ventas, double tarifa ) { super( nombre, apellido, nss ); establecerVentasBrutas( ventas ); establecerTarifaComision( tarifa ); } // fin del constructor de EmpleadoPorComision con cinco argumentos // establece la tarifa de comisin public void establecerTarifaComision( double tarifa ) { tarifaComision = ( tarifa > 0.0 && tarifa < 1.0 ) ? tarifa : 0.0; } // fin del mtodo establecerTarifaComision // devuelve la tarifa de comisin public double obtenerTarifaComision() { return tarifaComision; } // fin del mtodo obtenerTarifaComision // establece el monto de ventas brutas public void establecerVentasBrutas( double ventas ) { ventasBrutas = ( ventas < 0.0 ) ? 0.0 : ventas; } // fin del mtodo establecerVentasBrutas // devuelve el monto de ventas brutas public double obtenerVentasBrutas() { return ventasBrutas; } // fin del mtodo obtenerVentasBrutas
// calcula los ingresos; sobrescribe el mtodo abstracto ingresos en Empleado public double ingresos() { return obtenerTarifaComision() * obtenerVentasBrutas(); } // fin del mtodo ingresos // devuelve representacin String de un objeto EmpleadoPorComision public String toString() { return String.format( "%s: %s\n%s: $%,.2f; %s: %.2f", "empleado por comision", super.toString(), "ventas brutas", obtenerVentasBrutas(), "tarifa de comision", obtenerTarifaComision() ); } // fin del mtodo toString } // fin de la clase EmpleadoPorComision
// Fig. 10.8: EmpleadoBaseMasComision.java // La clase EmpleadoBaseMasComision extiende a EmpleadoPorComision. public class EmpleadoBaseMasComision extends EmpleadoPorComision { private double salarioBase; // salario base por semana // constructor con seis argumentos public EmpleadoBaseMasComision( String nombre, String apellido, String nss, double ventas, double tarifa, double salario ) { super( nombre, apellido, nss, ventas, tarifa ); establecerSalarioBase( salario ); // valida y almacena el salario base } // fin del constructor de EmpleadoBaseMasComision con seis argumentos // establece el salario base public void establecerSalarioBase( double salario ) { salarioBase = ( salario < 0.0 ) ? 0.0 : salario; // positivo } // fin del mtodo establecerSalarioBase // devuelve el salario base public double obtenerSalarioBase() { return salarioBase; } // fin del mtodo obtenerSalarioBase // calcula los ingresos; sobrescribe el mtodo ingresos en EmpleadoPorComision public double ingresos() { return obtenerSalarioBase() + super.ingresos(); } // fin del mtodo ingresos // devuelve representacin String de un objeto EmpleadoBaseMasComision public String toString() { return String.format( "%s %s; %s: $%,.2f", "con salario base", super.toString(), "salario base", obtenerSalarioBase() ); } // fin del mtodo toString } // fin de la clase EmpleadoBaseMasComision
// Fig. 10.9: PruebaSistemaNomina.java // Programa de prueba para la jerarqua de Empleado. public class PruebaSistemaNomina { public static void main( String args[] ) { // crea objetos de las subclases EmpleadoAsalariado empleadoAsalariado = new EmpleadoAsalariado( "John", "Smith", "111-11-1111", 800.00 ); EmpleadoPorHoras empleadoPorHoras = new EmpleadoPorHoras( "Karen", "Price", "222-22-2222", 16.75, 40 ); EmpleadoPorComision empleadoPorComision = new EmpleadoPorComision( "Sue", "Jones", "333-33-3333", 10000, .06 ); EmpleadoBaseMasComision empleadoBaseMasComision = new EmpleadoBaseMasComision( "Bob", "Lewis", "444-44-4444", 5000, .04, 300 ); System.out.println( "Empleados procesados por separado:\n" ); System.out.printf( "%s\n%s: $%,.2f\n\n", empleadoAsalariado, "ingresos", empleadoAsalariado.ingresos() ); System.out.printf( "%s\n%s: $%,.2f\n\n", empleadoPorHoras, "ingresos", empleadoPorHoras.ingresos() ); System.out.printf( "%s\n%s: $%,.2f\n\n", empleadoPorComision, "ingresos", empleadoPorComision.ingresos() ); System.out.printf( "%s\n%s: $%,.2f\n\n", empleadoBaseMasComision, "ingresos", empleadoBaseMasComision.ingresos() ); // crea un arreglo Empleado de cuatro elementos Empleado empleados[] = new Empleado[ 4 ]; // inicializa el arreglo con objetos Empleado empleados[ 0 ] = empleadoAsalariado; empleados[ 1 ] = empleadoPorHoras; empleados[ 2 ] = empleadoPorComision; empleados[ 3 ] = empleadoBaseMasComision; System.out.println( "Empleados procesados en forma polimorfica:\n" ); // procesa en forma genrica a cada elemento en el arreglo de empleados for ( Empleado empleadoActual : empleados ) { System.out.println( empleadoActual ); // invoca a toString // determina si el elemento es un EmpleadoBaseMasComision if ( empleadoActual instanceof EmpleadoBaseMasComision ) { // conversin descendente de la referencia de Empleado a una referencia de EmpleadoBaseMasComision EmpleadoBaseMasComision empleado = ( EmpleadoBaseMasComision ) empleadoActual; double salarioBaseAnterior = empleado.obtenerSalarioBase(); empleado.establecerSalarioBase( 1.10 * salarioBaseAnterior ); System.out.printf( "el nuevo salario base con 10%% de aumento es : $%,.2f\n", empleado.obtenerSalarioBase() ); } // fin de if System.out.printf( "ingresos $%,.2f\n\n", empleadoActual.ingresos() ); } // fin de for // obtiene el nombre del tipo de cada objeto en el arreglo de empleados for ( int j = 0; j < empleados.length; j++ ) System.out.printf( "El empleado %d es un %s\n", j, empleados[ j ].getClass().getName() ); } // fin de main } // fin de la clase PruebaSistemaNomina
Empleados procesados por separado: empleado asalariado: John Smith numero de seguro social: 111-11-1111 salario semanal: $800.00 ingresos: $800.00 empleado por horas: Karen Price numero de seguro social: 222-22-2222 sueldo por hora: $16.75; horas trabajadas: 40.00 ingresos: $670.00 empleado por comision: Sue Jones numero de seguro social: 333-33-3333 ventas brutas: $10,000.00; tarifa de comision: 0.06 ingresos: $600.00 con salario base empleado por comision: Bob Lewis numero de seguro social: 444-44-4444 ventas brutas: $5,000.00; tarifa de comision: 0.04; salario base: $300.00 ingresos: $500.00 Empleados procesados en forma polimorfica: empleado asalariado: John Smith numero de seguro social: 111-11-1111 salario semanal: $800.00 ingresos $800.00 empleado por horas: Karen Price numero de seguro social: 222-22-2222 sueldo por hora: $16.75; horas trabajadas: 40.00 ingresos $670.00 empleado por comision: Sue Jones numero de seguro social: 333-33-3333 ventas brutas: $10,000.00; tarifa de comision: 0.06 ingresos $600.00 con salario base empleado por comision: Bob Lewis numero de seguro social: 444-44-4444 ventas brutas: $5,000.00; tarifa de comision: 0.04; salario base: $300.00 el nuevo salario base con 10% de aumento es : $330.00 ingresos $530.00 El empleado 0 es un EmpleadoAsalariado El empleado 1 es un EmpleadoPorHoras El empleado 2 es un EmpleadoPorComision El empleado 3 es un EmpleadoBaseMasComision
// Fig. 10.11: PorPagar.java Declaracin de la interfaz PorPagar. public interface PorPagar { double obtenerMontoPago(); // calcula el pago; no hay implementacin } // fin de la interfaz PorPagar
// Fig. 10.12: Factura.java La clase Factura implementa a PorPagar. public class Factura implements PorPagar { private String numeroPieza; private String descripcionPieza; private int cantidad; private double precioPorArticulo; // constructor con cuatro argumentos public Factura( String pieza, String descripcion, int cuenta, double precio ) { numeroPieza = pieza; descripcionPieza = descripcion; establecerCantidad( cuenta ); // valida y almacena la cantidad establecerPrecioPorArticulo( precio ); // valida y almacena el precio por artculo } // find el constructor de Factura con cuatro argumentos public void establecerNumeroPieza( String pieza ) { // establece el nmero de pieza numeroPieza = pieza; } // fin del mtodo establecerNumeroPieza public String obtenerNumeroPieza() { // obtener nmero de pieza return numeroPieza; } // fin del mtodo obtenerNumeroPieza public void establecerDescripcionPieza( String descripcion ) { // establece la descripcin descripcionPieza = descripcion; } // fin del mtodo establecerDescripcionPieza public String obtenerDescripcionPieza() { // obtiene la descripcin return descripcionPieza; } // fin del mtodo obtenerDescripcionPieza public void establecerCantidad( int cuenta ) { // establece la cantidad cantidad = ( cuenta < 0 ) ? 0 : cuenta; // cantidad no puede ser negativa } // fin del mtodo establecerCantidad public int obtenerCantidad() { // obtener cantidad return cantidad; } // fin del mtodo obtenerCantidad // establece el precio por artculo public void establecerPrecioPorArticulo( double precio ) { precioPorArticulo = ( precio < 0.0 ) ? 0.0 : precio; // valida el precio } // fin del mtodo establecerPrecioPorArticulo public double obtenerPrecioPorArticulo() { // obtiene el precio por artculo return precioPorArticulo; } // fin del mtodo obtenerPrecioPorArticulo public String toString() { // devuelve representacin String de un objeto Factura return String.format( "%s: \n%s: %s (%s) \n%s: %d \n%s: $%,.2f", "factura", "numero de pieza", obtenerNumeroPieza(), obtenerDescripcionPieza(), "cantidad", obtenerCantidad(), "precio por articulo", obtenerPrecioPorArticulo() ); } // fin del mtodo toString // mtodo requerido para realizar el contrato con la interfaz PorPagar public double obtenerMontoPago() { return obtenerCantidad() * obtenerPrecioPorArticulo(); // calcula el costo total } // fin del mtodo obtenerMontoPago } // fin de la clase Factura
// Fig. 10.13: Empleado.java // La superclases abstracta Empleado implementa a PorPagar. public abstract class Empleado implements PorPagar { private String primerNombre; private String apellidoPaterno; private String numeroSeguroSocial; // constructor con tres argumentos public Empleado( String nombre, String apellido, String nss ) { primerNombre = nombre; apellidoPaterno = apellido; numeroSeguroSocial = nss; } // fin del constructor de Empleado con tres argumentos // establece el primer nombre public void establecerPrimerNombre( String nombre ) { primerNombre = nombre; } // fin del mtodo establecerPrimerNombre // devuelve el primer nombre public String obtenerPrimerNombre() { return primerNombre; } // fin del mtodo obtenerPrimerNombre // establece el apellido paterno public void establecerApellidoPaterno( String apellido ) { apellidoPaterno = apellido; } // fin del mtodo establecerApellidoPaterno // devuelve el apellido paterno public String obtenerApellidoPaterno() { return apellidoPaterno; } // fin del mtodo obtenerApellidoPaterno // establece el nmero de seguro social public void establecerNumeroSeguroSocial( String nss ) { numeroSeguroSocial = nss; // debe validar } // fin del mtodo establecerNumeroSeguroSocial // devuelve el nmero de seguro social public String obtenerNumeroSeguroSocial() { return numeroSeguroSocial; } // fin del mtodo obtenerNumeroSeguroSocial // devuelve representacin String de un objeto Empleado public String toString() { return String.format( "%s %s\nnumero de seguro social: %s", obtenerPrimerNombre(), obtenerApellidoPaterno(), obtenerNumeroSeguroSocial() ); } // fin del mtodo toString // Nota: Aqu no implementamos el mtodo obtenerMontoPago de PorPagar, as que // esta clase debe declararse como abstract para evitar un error de compilacin. } // fin de la clase abstracta Empleado
// Fig. 10.14: EmpleadoAsalariado.java // La clase EmpleadoAsalariado extiende a Empleado, que implementa a PorPagar. public class EmpleadoAsalariado extends Empleado { private double salarioSemanal; // constructor con cuatro argumentos public EmpleadoAsalariado( String nombre, String apellido, String nss, double salario ) { super( nombre, apellido, nss ); // pasa argumentos al constructor de Empleado establecerSalarioSemanal( salario ); // valida y almacena el salario } // fin del constructor de EmpleadoAsalariado con cuatro argumentos // establece el salario public void establecerSalarioSemanal( double salario ) { salarioSemanal = salario < 0.0 ? 0.0 : salario; } // fin del mtodo establecerSalarioSemanal // devuelve el salario public double obtenerSalarioSemanal() { return salarioSemanal; } // fin del mtodo obtenerSalarioSemanal // calcula los ingresos; implementa el mtodo de la interfaz PorPagar // que era abstracto en la superclase Empleado public double obtenerMontoPago() { return obtenerSalarioSemanal(); } // fin del mtodo obtenerMontoPago // devuelve representacin String de un objeto EmpleadoAsalariado public String toString() { return String.format( "empleado asalariado: %s\n%s: $%,.2f", super.toString(), "salario semanal", obtenerSalarioSemanal() ); } // fin del mtodo toString } // fin de la clase EmpleadoAsalariado
// Fig. 10.15: PruebaInterfazPorPagar.java Prueba la interfaz PorPagar. public class PruebaInterfazPorPagar { public static void main( String args[] ) { // crea arreglo PorPagar con cuatro elementos PorPagar objetosPorPagar[] = new PorPagar[ 4 ]; // llena el arreglo con objetos que implementan la interfaz PorPagar objetosPorPagar[ 0 ] = new Factura( "01234", "asiento", 2, 375.00 ); objetosPorPagar[ 1 ] = new Factura( "56789", "llanta", 4, 79.95 ); objetosPorPagar[ 2 ] = new EmpleadoAsalariado( "John", "Smith", "111-11-1111", 800.00 ); objetosPorPagar[ 3 ] = new EmpleadoAsalariado( "Lisa", "Barnes", "888-88-8888", 1200.00 ); System.out.println( "Facturas y Empleados procesados en forma polimorfica:\n" ); // procesa en forma genrica cada elemento en el arreglo objetosPorPagar for ( PorPagar porPagarActual : objetosPorPagar ) { // imprime porPagarActual y su monto de pago apropiado System.out.printf( "%s \n%s: $%,.2f\n\n", porPagarActual.toString(), "pago vencido", porPagarActual.obtenerMontoPago() ); } // fin de for } // fin de main } // fin de la clase PruebaInterfazPorPagar