martes, 31 de agosto de 2010
Si es Sábado o Domingo el alquiler de las revistas será 5, en otro días será 2.
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) );
}
}
lunes, 23 de agosto de 2010
Interfaces
Concepto de Interface
El concepto de Interface lleva un paso más adelante la idea de las clases abstractas. En Java una interface es una clase abstracta pura, es dcir una clase donde todos los métodos son abstractos (no se implementa ninguno). Permite al diseñador de clases establecer la forma de una clase (nombres de métodos, listas de argumentos y tipos de retorno, pero no bloques de código). Una interface puede también contener datos miembro, pero estos son siempre static y final. Una interface sirve para establecer un 'protocolo' entre clases.
Para crear una interface, se utiliza la palabra clave interface en lugar de class. La interface puede definirse public o sin modificador de acceso, y tiene el mismo significado que para las clases. Todos los métodos que declara una interface son siempre public.
Para indicar que una clase implementa los métodos de una interface se utiliza la palabra clave implements. El compilador se encargará de verificar que la clase efectivamente declare e implemente todos los métodos de la interface. Una clase puede implementar más de una interface.
Declaración y uso
Una interface se declara:
interface nombre_interface {
tipo_retorno nombre_metodo ( lista_argumentos ) ;
. . .
}
Por ejemplo:
interface InstrumentoMusical {
void tocar();
void afinar();
String tipoInstrumento();
}
Y una clase que implementa la interface:
class InstrumentoViento extends Object implements InstrumentoMusical {
void tocar() { . . . };
void afinar() { . . .};
String tipoInstrumento() {}
}
class Guitarra extends InstrumentoViento {
String tipoInstrumento() {
return "Guitarra";
}
}
La clase InstrumentoViento implementa la interface, declarando los métodos y escribiendo el código correspondiente. Una clase derivada puede también redefinir si es necesario alguno de los métodos de la interface.
Referencias a Interfaces
Es posible crear referencias a interfaces, pero las interfaces no pueden ser instanciadas. Una referencia a una interface puede ser asignada a cualquier objeto que implemente la interface. Por ejemplo:
InstrumentoMusical instrumento = new Guitarra();
instrumento.play();
System.out.prinln(instrumento.tipoInstrumento());
InstrumentoMusical i2 = new InstrumentoMusical(); //error.No se puede instanciar
Extensión de interfaces
Las interfaces pueden extender otras interfaces y, a diferencia de las clases, una interface puede extender más de una interface. La sintaxis es:
interface nombre_interface extends nombre_interface , . . . {
tipo_retorno nombre_metodo ( lista_argumentos ) ;
. . .
}
Agrupaciones de constantes
Dado que, por definición, todos los datos miembros que se definen en una interface son static y final, y dado que las interfaces no pueden instanciarse resultan una buena herramienta para implantar grupos de constantes. Por ejemplo:
public interface Meses {
int ENERO = 1 , FEBRERO = 2 . . . ;
String [] NOMBRES_MESES = { " " , "Enero" , "Febrero" , . . . };
}
Esto puede usarse simplemente:
System.out.println(Meses.NOMBRES_MESES[ENERO]);
Un ejemplo casi real
El ejemplo mostrado a continuación es una simplificación de como funciona realmente la gestión de eventos en el sistema gráfico de usuario soportado por el API de Java (AWT o swing). Se han cambiado los nombres y se ha simplificado para mostrar un caso real en que el uso de interfaces resuelve un problema concreto.
Supongamos que tenemos una clase que representa un botón de acción en un entorno gráfico de usuario (el típico botón de confirmación de una acción o de cancelación). Esta clase pertenecerá a una amplia jerarquía de clases y tendrá mecanismos complejos de definición y uso que no son objeto del ejemplo. Sin embargo podríamos pensar que la clase Boton tiene miembros como los siguientes.
class Boton extends . . . {
protected int x , y, ancho, alto; // posicion del boton
protected String texto; // texto del boton
Boton(. . .) {
. . .
}
void dibujar() { . . .}
public void asignarTexto(String t) { . . .}
public String obtenerTexto() { . . .)
. . .
}
Lo que aquí nos interesa es ver lo que sucede cuando el usuario, utilizando el ratón pulsa sobre el botón. Supongamos que la clase Boton tiene un método, de nombre por ejemplo click(), que es invocado por el gestor de ventanas cuando ha detectado que el usuario ha pulsado el botón del ratón sobre él. El botón deberá realizar alguna acción como dibujarse en posición 'pulsado' (si tiene efectos de tres dimensiones) y además, probablemente, querrá informar a alguien de que se ha producido la acción del usuario. Es en este mecanismo de 'notificación' donde entra el concepto de interface. Para ello definimos una interface Oyente de la siguiente forma:
interface Oyente {
void botonPulsado(Boton b);
}
La interface define un único método botonPulsado. La idea es que este método sea invocado por la clase Boton cuando el usuario pulse el botón. Para que esto sea posible en algún momento hay que notificar al Boton quien es el Oyente que debe ser notificado. La clase Boton quedaría:
class Boton extends . . . {
. . .
private Oyente oyente;
void registrarOyente(Oyente o) {
oyente = o;
}
void click() {
. . .
oyente.botonPulsado(this);
}
}
El método registrarOyente sirve para que alguien pueda 'apuntarse' como receptor de las acciones del usuario. Obsérvese que existe una referencia de tipo Oyente. A Boton no le importa que clase va a recibir su notificación. Simplemente le importa que implante la interface Oyente para poder invocar el método botonPulsado. En el método click se invoca este método. En el ejemplo se le pasa como parámetro una referencia al propio objeto Boton. En la realidad lo que se pasa es un objeto 'Evento' con información detallada de lo que ha ocurrido.
Con todo esto la clase que utiliza este mecanismo podría tener el siguiente aspecto:
class miAplicacion extends . . . implements Oyente {
public static main(String [] args) {
new miAplicacion(. . .);
. . .
}
. . .
miAplicacion(. . .) {
. . .
Boton b = new Boton(. . .);
b.registrarOyente(this);
}
. . .
void botonPulsado(Boton x) {
// procesar click
. . .
}
}
Obsérvese en el método registrarOyente que se pasa la referencia thisque en el lado de la clase Boton es recogido como una referencia a la interface Oyente. Esto es posible porque la clase miAplicacion implementa la interface Oyente . En términos clásicos de herencia miAplicacion ES un Oyente .
martes, 17 de agosto de 2010
Polimorfismo en Java
Antes de iniciar con el tema de polimorfismo....recordemos algunos conceptos
Declaración de la clase
La clase se declara mediante la línea public class Contador. En el caso más general, la declaración de una clase puede contener los siguientes elementos:
[public] [final | abstract] class Clase [extends ClaseMadre] [implements Interfase1 [, Interfase2 ]…]
o bien, para interfaces:
[public] interface Interfase [extends InterfaseMadre1 [, InterfaseMadre2 ]…]
Como se ve, lo único obligatorio es class y el nombre de la clase. Las interfases son un caso de clase particular que veremos más adelante.
Public, final o abstract
Definir una clase como pública (public) significa que puede ser usada por cualquier clase en cualquier paquete. Si no lo es, solamente puede ser utilizada por clases del mismo paquete (más sobre paquetes luego; básicamente, se trata de un grupo de clases e interfaces relacionadas, como los paquetes de biblioteca incluídos con Java).
Una clase final (final) es aquella que no puede tener clases que la hereden. Esto se utiliza básicamente por razones de seguridad (para que una clase no pueda ser reemplazada por otra que la herede), o por diseño de la aplicación.
Una clase abstracta (abstract) es una clase que puede tener herederas, pero no puede ser instanciada. Es, literalmente, abstracta (como la clase Number definida en java.lang). ¿Para qué sirve? Para modelar conceptos. Por ejemplo, la clase Number es una clase abstracta que representa cualquier tipo de números (y sus métodos no están implementados: son abstractos); las clases descendientes de ésta, como Integer o Float, sí implementan los métodos de la madre Number, y se pueden instanciar.
Por lo dicho, una clase no puede ser final y abstract a la vez (ya que la clase abstract requiere descendientes…)
¿Un poco complejo? Se va a entender mejor cuando veamos casos particulares, como las interfases (que por definición son abstractas ya que no implementan sus métodos).
Extends
La instrucción extends indica de qué clase desciende la nuestra. Si se omite, Java asume que desciende de la superclase Object.
Cuando una clase desciende de otra, esto significa que hereda sus atributos y sus métodos (es decir que, a menos que los redefinamos, sus métodos son los mismos que los de la clase madre y pueden utilizarse en forma transparente, a menos que sean privados en la clase madre o, para subclases de otros paquetes, protegidos o propios del paquete). Veremos la calificación de métodos muy pronto, a no desesperar!
Implements
Una interfase (interface) es una clase que declara sus métodos pero no los implementa; cuando una clase implementa (implements) una o más interfases, debe contener la implementación de todos los métodos (con las mismas listas de parámetros) de dichas interfases.
Esto sirve para dar un ascendiente común a varias clases, obligándolas a implementar los mismos métodos y, por lo tanto, a comportarse de forma similar en cuanto a su interfase con otras clases y subclases.
Interface
Una interfase (interface), como se dijo, es una clase que no implementa sus métodos sino que deja a cargo la implementación a otras clases. Las interfases pueden, asimismo, descender de otras interfases pero no de otras clases.
Todos sus métodos son por definición abstractos y sus atributos son finales (aunque esto no se indica en el cuerpo de la interfase).
Son útiles para generar relaciones entre clases que de otro modo no están relacionadas (haciendo que implementen los mismos métodos), o para distribuir paquetes de clases indicando la estructura de la interfase pero no las clases individuales (objetos anónimos).
Si bien diferentes clases pueden implementar las mismas interfases, y a la vez descender de otras clases, esto no es en realidad herencia múltiple ya que una clase no puede heredar atributos ni métodos de una interface; y las clases que implementan una interfase pueden no estar ni siquiera relacionadas entre sí.
El polimorfismo es un concepto de la programación orientada a objetos que nos permite programar en forma general, en lugar de hacerlo en forma específica. En general nos sirve para programar objetos con características comunes y que todos estos compartan la misma superclase en una jerarquía de clases, como si todas fueran objetos de la superclase. Esto nos simplifica la programación.
Recuerde el ejemplo del ecosistema, en donde todos los objetos de las distintas especies heredaban de una superclase llamada Animal, que brindaba la información general de cualquier animal, independiente de su especie. Sin embargo, cada especie hace un uso particular de cada uno de los métodos u operaciones de la clase Animal. El método comer() no se ejecutará de la misma manera en un León() o en un Pavo(). Lo mismo ocurre para métodos moverse() en objetos de tipo Tiburón() o Gallina(), aunque todas las especies realicen estos métodos. A la sobrescritura o implementación específica de métodos es la clave del polimorfismo.
Para poner en práctica se hará un ejemplo bastante sencillo. Se hará una librería de clases que represente figuras tridimensionales y bidimensionales, y su respectiva jerarquía de clases. Las clases deben ser capaces de tener funcionamiento bastante básico, como obtener áreas, volúmenes y perímetros de la figura correspondiente.
La representación de la jerarquía sería como ésta:
Click a la imagen para verla más grande...
La superclase de dicha jerarquía podría ser muy parecida a ésta:
public abstract class figura {
protected String nombre;
protected int color;
protected int grosorBorde;
public String getNombre(){
return this.nombre;
}
public void setNombre(String n){
this.nombre=n;
}
public int getColor(){
return this.color;
}
public void setColor(int c){
this.color=c;
}
public int getGrosorBorde(){
return this.grosorBorde;
}
public void setGrosorBorde(int g){
this.grosorBorde=g;
}
public abstract void dibujar();
}
Las siguientes clases en el nivel de la jerarquía podrían quedar muy parecidas a éstas:
public abstract class figura2D extends figura {
public abstract int calcularArea();
public abstract int calcularPerimetro();
}
public abstract class figura3D extends figura {
public abstract int calcularVolumen();
}
Se le pide que forme las clases de la parte inferior de la jerarquía y que representarían los objetos a instanciarse.
Además, debe de realizar una implementación de esta librería, en donde el usuario pueda crear nuevas figuras y que éstas se almacenen en un arreglo de figuras.
Ejercicios:
* Realice una jerarquía de clases en donde se representen un cuadrilátero.
* Mediante esa superclase, realice las clases rectángulo, cuadrado, paralelogramo y trapezoide; que heredarán de ella, convirtiéndose prácticamente en objetos cuadrilátero (superclase).
martes, 10 de agosto de 2010
Taller HERENCIA -10 -08-2010
1.)Crea una clase CRectángulo. La clase tiene dos datos miembro, largo y ancho. La clase cuenta con
métodos que calculan el perímetro (obtenPerímetro) y el área (obtenÁrea) del rectángulo, así como
métodos para obtener y poner los valores correspondientes a los atributos largo y ancho. Los métodos
poner deben comprobar que tanto largo como ancho sean mayores que 0 y menores que 20.
Probar y analizar
2.)Construir una clase final Math2 que amplíe las declaraciones de métodos estáticos de la clase
Math y que incluya funciones que devuelvan, respectivamente, el máximo, el mínimo, el
sumatorio, la media aritmética y la media geométrica de un array de números reales dado como
parámetro.
Solucion....
public final class Math2 {
public static double min (double [] v) {
double menor = v[0];
for (int i=0; i<"v.length"; i++) {
if (menor>v[i]) { menor=v[i]; }
}
return menor;
}
public static double max (double [] v) {
double mayor = v[0];
for (int i=0; i<"v.length"; i++) {
if (mayor
return mayor;
}
public static double sum (double [] v) {
double sumatorio =0.0;
for (int i=0; i<"v.length"; i++) {
sumatorio=sumatorio+v[i];
}
return sumatorio;
}
public static double mediaAritmetica (double [] v) {
double sumatorio =0.0;
for (int i=0; i<"v.length"; i++) {
sumatorio+=v[i];
}
return (sumatorio/"v.length");
}
public static double mediaGeometrica (double [] v) {
double producto =1.0;
for (int i=0; i<"v.length"; i++) {
producto*=v[i];
}
return Math.sqrt(producto);
}
}
Ejemplo de uso de la clase Math2:
/**
* Programa PruebaMath2
* A. Garcia-Beltran - diciembre, 2004
*/
public class PruebaMath2 {
public static void main (String [] args) {
double [] x = new double[10];
for (int i=0; i<"x.length"; i++) {
x[i] = 5*Math.random();
System.out.println("x["+i+"] = "+x[i]);
}
System.out.println("Minimo : " + Math2.min(x));
System.out.println("Maximo : " + Math2.max(x));
System.out.println("Sumatorio : " + Math2.sum(x));
System.out.println("Media arit. : " + Math2.mediaAritmetica(x));
System.out.println("Media geom. : " + Math2.mediaGeometrica(x));
}
}
Salida por pantalla de la ejecución del código anterior:
$>java PruebaMath2
x[0] = 3.9295964172220037
x[1] = 3.3033982387050598
x[2] = 0.38413320538334517
x[3] = 1.441533972096129
x[4] = 0.5194720253569335
x[5] = 0.5801662478628711
x[6] = 4.370186448410964
x[7] = 1.2908820504663692
x[8] = 3.8520244358555944
x[9] = 0.1450533020411976
Minimo : 0.1450533020411976
Maximo : 4.370186448410964
Sumatorio : 19.816446343400468
Media arit. : 1.981644634340047
Media geom. : 2.6131642597311173
3.)Diseñe una clase cualquiera donde se muestre un ejemplo de sobrecarga de metodos...
miércoles, 4 de agosto de 2010
Ejercicios Herencia
1.)Construir una clase Factura que descienda de la clase Precio y que incluya dos atributos
específicos llamados emisor y cliente y, al menos, un método llamado
imprimirFactura.
Solucion.
Clase Factura descendiente de la clase Precio y que incluye dos atributos específicos
llamados emisor y cliente y un método llamado imprimirFactura....Usted segun esta solucion debe construir la clase precio.
/**
* Declaracion de la clase Factura
* descendiente de la clase precio
*/
public class Factura extends Precio {
public int cliente;
private final String emisor = "Almacenes ACME S.A";
public void imprimirFactura () {
System.out.println("");
System.out.println("Emisor: " + emisor);
System.out.println("----------------------");
System.out.println("Cliente: " + cliente);
System.out.println("Total: " + euros + " euros");
}
}
Ejemplo de uso de la clase Factura:
/**
* Programa PruebaFactura
* A. Garcia-Beltran - diciembre, 2004
*/
public class PruebaFactura {
public static void main (String [] args) {
Factura f = new Factura();
f.cliente = 12345;
f.pone(1000);
f.imprimirFactura();
}
}
Salida por pantalla de la ejecución del código anterior:
$>java pruebaFactura
Emisor: Almacenes ACME S.A
----------------------
Cliente: 12345
Total: 1000.0 euros
______________________________________________________________________
2.) Una caja registradora es un aparato mecánico o electrónico que permite calcular y registrar transacciones comerciales, e incluye un cajón para guardar dinero. Por lo general, permite además imprimir un recibo o factura para el cliente, según las disposiciones legales de cada País. Crear una clase caja registradora que dependa de una clase padre calculadore(Suma pollos)....
martes, 3 de agosto de 2010
Herencia en java (II)
Herencia - II
El modificador de acceso protected
El modificador de acceso protected es una combinación de los accesos que proporcionan los modificadores public y private. protected proporciona acceso público para las clases derivadas y acceso privado (prohibido) para el resto de clases.
Por ejemplo, si en la clase Empleado definimos:
class Empleado {
protected int sueldo;
. . .
}
entonces desde la clase Ejecutivo se puede acceder al dato miembro sueldo, mientras que si se declara private no.
Up-casting y Down-casting
Siguiendo con el ejemplo de los apartados anteriores, dado que un Ejecutivo ES un Empleado se puede escribir la sentencia:
Empleado emp = new Ejecutivo("Máximo Dueño" , 2000);
Aquí se crea un objeto de la clase Ejecutivo que se asigna a una referencia de tipo Empleado. Esto es posible y no da error ni al compilar ni al ejecutar porque Ejecutivo es una clase derivada de Empleado. A esta operación en que un objeto de una clase derivada se asigna a una referencia cuyo tipo es alguna de las superclases se denomina 'upcasting'.
Cuando se realiza este tipo de operaciones, hay que tener cuidado porque para la referencia emp no existen los miembros de la clase Ejecutivo, aunque la referencia apunte a un objeto de este tipo. Así, las expresiones:
emp.aumentarSueldo(3); // 1. ok. aumentarSueldo es de Empleado
emp.asignarPresupuesto(1500); // 2. error de compilación
En la primera expresión no hay error porque el método aumentarSueldo está definido en la clase Empleado. En la segunda expresión se produce un error de compilación porque el método asignarPresupuesto no existe para la clase Empleado.
Por último, la situación para el método toString es algo más compleja. Si se invoca el método:
emp.toString(); // se invoca el metodo toString de Ejecutivo
el método que resultará llamado es el de la clase Ejecutivo. toString existe tanto para Empleado como para Ejecutivo, por lo que el compilador Java no determina en el momento de la compilación que método va a usarse. Sintácticamente la expresión es correcta. El compilador retrasa la decisión de invocar a un método o a otro al momento de la ejecución. Esta técnica se conoce con el nombre de dinamic binding o late binding. En el momento de la ejecución la JVM comprueba el contenido de la referencia emp. Si apunta a un objeto de la clase Empleado invocará al método toString de esta clase. Si apunta a un objeto Ejecutivo invocará por el contrario al método toString de Ejecutivo.
Operador cast
Si se desea acceder a los métodos de la clase derivada teniendo una referencia de una clase base, como en el ejemplo del apartado anterior hay que convertir explicitamente la referencia de un tipo a otro. Esto se hace con el operador de cast de la siguiente forma:
Empleado emp = new Ejecutivo("Máximo Dueño" , 2000);
Ejecutivo ej = (Ejecutivo)emp; // se convierte la referencia de tipo
ej.asignarPresupuesto(1500);
La expresión de la segunda línea convierte la referencia de tipo Empleado asignándola a una referencia de tipo Ejecutivo. Para el compilador es correcto porque Ejecutivo es una clase derivada de Empleado. En tiempo de ejecución la JVM convertirá la referencia si efectivamente emp apunta a un objeto de la clase Ejecutivo. Si se intenta:
Empleado emp = new Empleado("Javier Todudas" , 2000);
Ejecutivo ej = (Ejecutivo)emp;
no dará problemas al compilar, pero al ejecutar se producirá un error porque la referencia emp apunta a un objeto de clase Empleado y no a uno de clas Ejecutivo.
La clase Object
En Java existe una clase base que es la raíz de la jerarquía y de la cual heredan todas aunque no se diga explicitamente mediante la clausula extends. Esta clase base se llama Object y contiene algunos métodos básicos. La mayor parte de ellos no hacen nada pero pueden ser redefinidos por las clases derivadas para implementar comportamientos específicos. Los métodos declarados por la clase Object son los siguientes:
public class Object {
public final Class getClass() { . . . }
public String toString() { . . . }
public boolean equals(Object obj) { . . . }
public int hashCode() { . . . }
protected Object clone() throws CloneNotSupportedException { . . . }
public final void wait() throws IllegalMonitorStateException,
InterruptedException { . . . }
public final void wait(long millis) throws IllegalMonitorStateException,
InterruptedException {. . .}
public final void wait(long millis, int nanos) throws
IllegalMonitorStateException,
InterruptedException { . . . }
public final void notify() throws IllegalMonitorStateException { . . . }
public final void notifyAll() throws
IllegalMonitorStateException { . . . }
protected void finalize() throws Throwable { . . . }
}
Las cláusulas final y throws se verán más adelante. Como puede verse toString es un método de Object, que puede ser redefinido en las clases derivadas. Los métodos wait, notify y notifyAll tienen que ver con la gestión de threads de la JVM. El método finalize ya se ha comentado al hablar del recolector de basura.
Para una descripción exahustiva de los métodos de Object se puede consultar la documentación de la API del JDK.
La cláusula final
En ocasiones es conveniente que un método no sea redefinido en una clase derivada o incluso que una clase completa no pueda ser extendida. Para esto está la cláusula final, que tiene significados levemente distintos según se aplique a un dato miembro, a un método o a una clase.
Para una clase, final significa que la clase no puede extenderse. Es, por tanto el punto final de la cadena de clases derivadas. Por ejemplo si se quisiera impedir la extensión de la clase Ejecutivo, se pondría:
final class Ejecutivo {
. . .
}
Para un método, final significa que no puede redefinirse en una clase derivada. Por ejemplo si declaramos:
class Empleado {
. . .
public final void aumentarSueldo(int porcentaje) {
. . .
}
. . .
}
entonces la clase Ejecutivo, clase derivada de Empleado no podría reescribir el método aumentarSueldo, y por tanto cambiar su comportamiento.
Para un dato miembro, final significa también que no puede ser redefinido en una clase derivada, como para los métodos, pero además significa que su valor no puede ser cambiado en ningún sitio; es decir el modificador final sirve también para definir valores constantes. Por ejemplo:
class Circulo {
. . .
public final static float PI = 3.141592;
. . .
}
En el ejemplo se define el valor de PI como de tipo float, estático (es igual para todas las instancias), constante (modificador final) y de acceso público.
Herencia simple
Java incorpora un mecanismo de herencia simple. Es decir, una clase sólo puede tener una superclase directa de la cual hereda todos los datos y métodos. Puede existir una cadena de clases derivadas en que la clase A herede de B y B herede de C, pero no es posible escribir algo como:
class A extends B , C .... // error
Este mecanismo de herencia múltiple no existe en Java.
Java implanta otro mecanismo que resulta parecido al de herencia múltiple que es el de las interfaces que se verá más adelante.
lunes, 2 de agosto de 2010
Herencia en java
Herencia
Composición
En anteriores ejemplos se ha visto que una clase tiene datos miembro que son instancias de otras clases. Por ejemplo:
class Circulo {
Punto centro;
int radio;
float superficie() {
return 3.14 * radio * radio;
}
}
Esta técnica en la que una clase se compone o contiene instancias de otras clases se denomina composición. Es una técnica muy habitual cuando se diseñan clases. En el ejemplo diríamos que un Circulo tiene un Punto (centro) y un radio.
Herencia
Pero además de esta técnica de composición es posible pensar en casos en los que una clase es una extensión de otra. Es decir una clase es como otra y además tiene algún tipo de característica propia que la distingue. Por ejemplo podríamos pensar en la clase Empleado y definirla como:
class Empleado {
String nombre;
int numEmpleado , sueldo;
static private int contador = 0;
Empleado(String nombre, int sueldo) {
this.nombre = nombre;
this.sueldo = sueldo;
numEmpleado = ++contador;
}
public void aumentarSueldo(int porcentaje) {
sueldo += (int)(sueldo * aumento / 100);
}
public String toString() {
return "Num. empleado " + numEmpleado + " Nombre: " + nombre +
" Sueldo: " + sueldo;
}
}
En el ejemplo el Empleado se caracteriza por un nombre (String) y por un número de empleado y sueldo (enteros). La clase define un constructor que asigna los valores de nombre y sueldo y calcula el número de empleado a partir de un contador (variable estática que siempre irá aumentando), y dos métodos, uno para calcular el nuevo sueldo cuando se produce un aumento de sueldo (método aumentarSueldo) y un segundo que devuelve una representación de los datos del empleado en un String.(método toString).
Con esta representación podemos pensar en otra clase que reuna todas las características de Empleado y añada alguna propia. Por ejemplo, la clase Ejecutivo. A los objetos de esta clase se les podría aplicar todos los datos y métodos de la clase Empleado y añadir algunos, como por ejemplo el hecho de que un Ejecutivo tiene un presupuesto.
Así diriamos que la clase Ejecutivo extiende o hereda la clase Empleado. Esto en Java se hace con la clausula extends que se incorpora en la definición de la clase, de la siguiente forma:
class Ejecutivo extends Empleado {
int presupuesto;
void asignarPresupuesto(int p) {
presupuesto = p;
}
}
Con esta definición un Ejecutivo es un Empleado que además tiene algún rasgo distintivo propio. El cuerpo de la clase Ejecutivo incorpora sólo los miembros que son específicos de esta clase, pero implícitamente tiene todo lo que tiene la clase Empleado.
A Empleado se le llama clase base o superclase y a Ejecutivo clase derivada o subclase.
Los objetos de las clases derivadas se crean igual que los de la clase base y pueden acceder tanto sus datos y métodos como a los de la clase base. Por ejemplo:
Ejecutivo jefe = new Ejecutivo( "Armando Mucho", 1000);
jefe.asignarPresupuesto(1500);
jefe.aumentarSueldo(5);
Nota: La discusión acerca de los constructores la veremos un poco más adelante.
Atención!: Un Ejecutivo ES un Empleado, pero lo contrario no es cierto. Si escribimos:
Empleado curri = new Empleado ( "Esteban Comex Plota" , 100) ;
curri.asignarPresupuesto(5000); // error
se producirá un error de compilación pues en la clase Empleado no existe ningún método llamado asignarPresupuesto.
Redefinición de métodos. El uso de super.
Además se podría pensar en redefinir algunos métodos de la clase base pero haciendo que métodos con el mismo nombre y características se comporten de forma distinta. Por ejemplo podríamos pensar en rediseñar el método toString de la clase Empleado añadiendo las características propias de la clase Ejecutivo. Así se podría poner:
class Ejecutivo extends Empleado {
int presupuesto;
void asignarPresupuesto(int p) {
presupuesto = p;
}
public String toString() {
String s = super.toString();
s = s + " Presupuesto: " + presupuesto;
return s;
}
}
De esta forma cuando se invoque jefe.toString() se usará el método toString de la clase Ejecutivo en lugar del existente en la clase Empleado.
Observese en el ejemplo el uso de super, que representa referencia interna implícita a la clase base (superclase). Mediante super.toString() se invoca el método toString de la clase Empleado
Inicialización de clases derivadas
Cuando se crea un objeto de una clase derivada se crea implicitamente un objeto de la clase base que se inicializa con su constructor correspondiente. Si en la creación del objeto se usa el constructor no-args, entonces se produce una llamada implicita al constructor no-args para la clase base. Pero si se usan otros constructores es necesario invocarlos explicitamente.
En nuestro ejemplo dado que la clase método define un constructor, necesitaremos también un constructor para la clase Ejecutivo, que podemos completar así:
class Ejecutivo extends Empleado {
int presupuesto;
Ejecutivo (String n, int s) {
super(n,s);
}
void asignarPresupuesto(int p) {
presupuesto = p;
}
public String toString() {
String s = super.toString();
s = s + " Presupuesto: " + presupuesto;
return s;
}
}
Observese que el constructor de Ejecutivo invoca directamente al constructor de Empleado mediante super(argumentos). En caso de resultar necesaria la invocación al constructor de la superclase debe ser la primera sentencia del constructor de la subclase.