Professional Documents
Culture Documents
ESCRITO CINCO.
SUBCLASES.
Hasta este momento slo hemos abordado la abstraccin y el encapsulamiento. En
este escrito nos dedicaremos a trabajar la herencia. sta provee el mecanismo ms
simple para especificar una forma alternativa de acceso a una clase existente, o
para definir una nueva clase que aada nuevas caractersticas a una clase existente.
Esta nueva clase se denomina subclase o clase derivada de la clase existente,
superclase o clase base. Las clases que estn en la parte inferior en la jerarqua
heredan de las clases que estn en la parte superior de la jerarqua.
En una jerarqua de clases, una clase es tanto ms especializada cuanto ms alejada
est de la raz. Y es tanto ms genrica cuando ms cerca est de la raz. Por
ejemplo, la clase Object es la raz de la jerarqua de las clases de Java y slo define
los atributos y comportamientos comunes a todas las clases.
Generalmente, la razn de la existencia de una clase genrica es proporcionar los
atributos y comportamientos que sern compartidos por todas las subclases. Una
clase que se comporta de esta forma se denomina abstracta y se define como tal
calificndola explcitamente como abstract.
Una clase abstracta puede contener el mismo tipo de miembros que una clase que
no lo sea, y adems contener mtodos abstractos, que una clase no abstracta no
puede contener, pero no se permite crear objetos de ella.
Un mtodo calificado como abstracto tiene la particularidad de no tener cuerpo y
la idea es proporcionar mtodos que deben ser redefinidos en las subclases que
pertenecen a la clase abstracta con la intencin de adaptarlos a las necesidades
particulares de stas.
Supongamos la siguiente jerarqua:
Clase CCuenta
Clase CCuentaCorriente
Clase CCuentaAhorro
104
Significado
nombre
cuenta
saldo
tipoDeInteres
Mtodo
Significado
CCuenta
105
// Mtodos
public CCuenta() {};
public CCuenta(String nom, String cue, double sal, double tipo){
asignarNombre(nom);
asignarCuenta(cue);
ingreso(sal);
asignarTipoDeInters(tipo);
}
public void asignarNombre(String nom){
if (nom.length() == 0){
System.out.println("Error: cadena vaca");
return;
}
nombre = nom;
}
public String obtenerNombre(){
return nombre;
}
public void asignarCuenta(String cue){
if (cue.length() == 0){
System.out.println("Error: cuenta no vlida");
return;
}
cuenta = cue;
}
public String obtenerCuenta(){
return cuenta;
}
public double estado(){
return saldo;
}
public abstract void comisiones();
public abstract double intereses();
public void ingreso(double cantidad) {
if (cantidad < 0){
System.out.println("Error: cantidad negativa");
return;
}
saldo += cantidad;
}
public void reintegro(double cantidad){
106
107
Java provee las palabras clave private, protected, public o omitirse (acceso
predeterminado). La tabla siguiente resume de una forma clara qu clases, o
subclases pueden acceder a los miembros de otra clase, dependiendo del control de
acceso especifico.
Su misma clase
Si
Si
Si
Si
No
Si
Si
Si
No
No
No
Si
No
No
Si
Si
Los siguientes puntos resumen las reglas a tener en cuenta cuando se define una
subclase:
1. Una subclase hereda todos los miembros de su superclase, excepto los
constructores. Una consecuencia inmediata de esto es que la estructura
interna de datos de un objeto de una subclase, estar formada por los
atributos que ella define y por los heredados de su superclase. Una subclase
no tiene acceso directo a los miembros privados de su superclase. Una
subclase si puede acceder directamente a los miembros pblicos y
protegidos de su superclase, tambin puede acceder a los miembros
predeterminados
2. Una subclase puede aadir sus propios atributos y mtodos. Si el nombre de
alguno de estos miembros coincide con el de un miembro heredado, este
ltimo queda oculto para la subclase, que se traduce en que la subclase ya
no puede acceder directamente a ese miembro.
3. Los miembros heredados por una subclase pueden, a su vez, ser heredados
por ms subclases de ella. A esto se le llama programacin de herencia.
Continuando con el ejemplo, se disea una nueva clase CCuentaAhorro que tenga
adems de las mismas capacidades de CCuenta, las siguientes:
Atributo
CuotaMantenimiento
Significado
Dato del tipo double que almacena la comisin que
Cobrar el banco por mantenimiento de la cuenta.
Mtodo
CCuentaAhorro
Significado
Es el constructor de la clase. Inicia los atributos de la
misma
Establece la cuota de mantenimiento de la cuenta.
Devuelve la cuota de mantenimiento de la cuenta.
asignarCuotaManten
obtenerCuotaManten
108
comisiones
Intereses
109
// Acumular los intereses por mes slo los das 1 de cada mes
double interesesProducidos = 0.0;
interesesProducidos = estado() * obtenerTipoDeInters() / 1200.0;
ingreso(interesesProducidos);
// Devolver el inters mensual por si fuera necesario
return interesesProducidos;
}
}
//////////////////////////////////////////////////////////////////
Observe que al definir una subclase se aade la palabra reservada extends y el
nombre de la superclase. En la definicin de la subclase se describen las
caractersticas adicionales que la distinguen de la superclase.
Los constructores de la clase CCuenta no se heredan, puesto que cada clase define
el suyo por omisin.
Una pequea aplicacin basada en una clase Test que cree un objeto del tipo
CCuentaAhorro es:
/////////////////////////////////////////////////////////////////
// Aplicacin para trabajar con CCuenta...
//
PROGRAMA 5.3
//
public class Test{
public static void main(String[] args){
CCuentaAhorro cliente01 = new CCuentaAhorro();
CCuentaAhorro cliente02 = new CCuentaAhorro("Un nombre", "Una
cuenta", 1000000, 3.5, 300);
cliente01.asignarNombre("Un nombre");
cliente01.asignarCuenta("Una cuenta");
cliente01.asignarTipoDeInters(2.5);
cliente01.asignarCuotaManten(300);
cliente01.ingreso(1000000);
cliente01.reintegro(500000);
cliente01.comisiones();
System.out.println(cliente01.obtenerNombre());
System.out.println(cliente01.obtenerCuenta());
System.out.println(cliente01.estado());
System.out.println(cliente01.obtenerTipoDeInters());
}
}
Partiendo del hecho de que las clases CCuenta y CCuentaAhorro pertenecen al
mismo paquete que la clase Test. Entonces un objeto, como cliente01, de la clase
CCuentaAhorro puede invocar cualquier mtodo pblico, protegido y
predeterminado de CCuentaAhorro y de CCuenta, pero no tiene acceso a sus
110
}
public int mtodo_x() {
System.out.println("Estoy en mtodo_x de la ClaseB donde: return atributo_x*-10");
112
113
//. . .
}
}
Cuando se crea un objeto de una subclase, por ejemplo cliente01 o cliente02,
primero se construye la porcin del objeto correspondiente a su superclase y a
continuacin la porcin del objeto correspondiente a la subclase.
114
Clase CCuenta
Clase CCuentaCorriente
Clase CCuentaAhorro
Clase CCuentaCorrienteConIn
Para implementar una subclase como CCuentaCorrienteConIn, nos es suficiente
conocer a fondo la superclase CCuentaCorriente sin importarnos CCuenta
115
La clase CCuentaCorriente es una nueva clase que hereda de la clase CCuenta. Por
lo tanto, tendr todos los miembros de su superclase, a los que se aadir lo
siguiente:
Atributo
Transacciones
importePorTrans
transExentas
Mtodo
CCuentaCorriente
decrementarTransacciones
asignarImportePorTrans
obtenerImportePorTrans
asignarTransExentas
obtenerTransExentas
ingreso
reingreso
Comisiones
intereses
Significado
Dato de tipo int que almacena el nmero de
Transacciones efectuadas sobre esa cuenta.
Dato de tipo double que almacena el
importe que la entidad bancaria cobrar por
cada transaccin
Dato de tipo int que almacena el nmero de
transacciones gratuitas.
Significado.
Constructor de la clase
Decrementa en uno en nmero de
transacciones.
Establecer el importe por transaccin
Devuelve el importe por transaccin.
Establecer el nmero de transacciones exentas.
Devuelve el nmero de transacciones exentas.
Aadede la cantidad especifica al saldo actual
de la cuenta e incrementa el nmero de
transacciones
Resta la cantidad especifica del saldo actual de
la cuenta e incrementa el nmero de
transacciones.
Se ejecuta el da uno de cada mes para cobrar
El importe de las transacciones efectuadas y
no estn exentas y pone el nmero de
transacciones a cero.
Se ejecuta los das uno de cada mes para
calcular el importe correspondiente a los
a los intereses por mes y se aade al saldo
116
invoca al
// inicia
// inicia
// inicia
constructor CCuenta
transacciones
importePorTrans
transExentas
117
// Acumular los intereses por mes slo los das 1 de cada mes
double interesesProducidos = 0.0;
// Hasta 3000 pesos al 0.5%. El resto al inters establecido.
if (estado() <= 3000)
interesesProducidos = estado() * 0.5 / 1200.0;
else{
interesesProducidos = 3000 * 0.5 / 1200.0 +
(estado() - 3000) * obtenerTipoDeInters() / 1200.0;
}
ingreso(interesesProducidos);
// Este ingreso no debe incrementar las transacciones
decrementarTransacciones();
Ahora supongamos que queremos tener otra cuenta corriente en el cual inicie a
acumular intereses a partir de los 3000 pesos. Segn esto, CuentaCorrienteConIn,
adems de los miembros heredados, slo precisa implementar sus constructores y
variar el mtodo intereses:
Mtodo
CCuentaCorriente
intereses
Significado
Constructor de la clase.
Calcula el importe/mes correspondiente a los
intereses producidos. Precisa un saldo mnimo
de 3000 pesos.
118
ingreso(interesesProducidos);
// Este ingreso no debe incrementar las transacciones
decrementarTransacciones();
}
Observe que la referencia a objeto cliente01, despus de ser construido, responde
algunos mensajes. Algunos heredados de su superclase, como obtenerNombre, y
otros propios, como intereses.
La referencia a objeto cliente02, despus de ser construido, responde a algunos
mensajes, algunos heredados de su superclase directa, como reintegro, otros
heredados de su superclase indirecta, como asignarNombre, y otros propios, como
intereses.
El comportamiento de un miembro del tipo esttico en cuanto a herencia se
refiere es el mismo que el de los otros miembros, pero teniendo presente que son
miembros de clase; y aunque se trate de mtodos, tambin pueden ser redefinidos,
aunque, en este caso, el nombre de la clase indicar la versin del mtodo que se
invocar. Si existe solo un mtodo tipo esttico con el mismo nombre en una
superclase, se mantendr una nica copia que podran usar las subclases.
CONVERSIONES IMPLCITAS.
Observe el siguiente ejemplo:
//////////////////////////////
/ / / / / PROGRAMA 5.8
public class Test{
public static void main(String[] arg){
CCuentaCorriente cliente01;
Cliente01=
new CCuentaCorrienteConIn(cliente, 1234567890,1000, 3.5, 1.0, 6);
String cuenta=cliente01.obtenerCuenta();
//. . .
}
}
En este caso, Java permite convertir en forma implcita una referencia a un objeto
de una subclase en una referencia a su superclase directa o indirectamente.
Otro ejemplo es:
//////////////////////////
/ / / / /PROGRAMA 5.9
class Test3{
public static void main(String[] arg){
CCuenta cliente;
CCuentaCorriente cliente01=
new CCuentaCorriente("cliente01", "1234567891", 1000, 3.5, 1.0, 6);
CCuentaCorrienteConIn cliente02=
new CCuentaCorrienteConIn("cliente02", "1234567892",2000, 2.0, 1.0, 6);
cliente =cliente01;
String nombre=cliente.obtenerNombre();
System.out.println("nombre cliente01="+nombre);
cliente=cliente02;
String cuenta=cliente.obtenerCuenta();
120
System.out.println("cuenta cliente02="+cuenta);
}
}
En este caso, cliente es una referencia a objeto del tipo CCuenta. Esta referencia se
utiliza para hacer referencia en forma indistinta a objetos del tipo
CCuentaCorriente y CCuentaCorrienteConIn.
Cuando accedemos a un objeto de una subclase por medio de una referencia a su
supeclase, ese objeto slo puede ser manipulado por los mtodos de su
superclase (la referencia es slo para los mtodos a los que ha sido declarada hacia
arriba, sus superclases). Ejemplo:
///////////////////////////////////
//////////PROGRAMA 5.10
class Test4{
public static void main(String[] arg){
CCuenta cliente;
CCuentaCorriente cliente01=
new CCuentaCorriente("cliente01", "1234567891", 1000, 3.5, 1.0, 6);
CCuentaCorrienteConIn cliente02=
new CCuentaCorrienteConIn("cliente02", "1234567892",2000, 2.0, 1.0, 6);
cliente =cliente01;
cliente.asignarNombre("Hola");
cliente.asignarImportePorTrans(1.0); //error: No es un mtodo de CCuenta
cliente=cliente02;
String cuenta=cliente.obtenerCuenta();
cliente.asignarTransExentas(19);//error: No es un mtodode CCuenta
}
}
Este ejemplo, al compilarlo, produce dos errores ya que al intentar acceder a los
mtodos asignarImportePorTrans y asignarTransExentas ocasiona que el tipo de
variable cliente, que es una referencia a CCuenta, slo puede recibir mensajes de
la clase de dicha variable.
Cuando se invoca a un mtodo que est definido en la superclase y redefinido en
sus subclases, la versin que se ejecuta depende de la clase del objeto referenciado,
no del tipo de variable que lo referencia. Por ejemplo:
////////////////////////
/////////PROGRAMA 5.11
class Test5{
public static void main(String[] arg){
CCuenta cliente;
CCuentaCorriente cliente01=
new CCuentaCorriente("cliente01", "1234567891", 1000, 3.5, 1.0, 6);
CCuentaCorrienteConIn cliente02=
new CCuentaCorrienteConIn("cliente02", "1234567892",2000, 2.0, 1.0, 6);
cliente =cliente01;
double intereses;
intereses=cliente.intereses(); //CCuentaCorriente.intereses()
121
System.out.println("intereses cliente01="+intereses);
cliente=cliente02;
intereses=cliente.intereses();//CuentaCorrienteConIn.intereses()
System.out.println("intereses cliente02="+intereses);
}
}
CONVERSIONES EXPLCITAS.
Una referencia de un objeto de la superclase a una referencia a su subclase, no se
puede hacer, aunque se fuerce a ello utilizando una construccin cast, excepto
cuando el objeto al que se tiene acceso a travs de la referencia a la superclase es
un objeto de la subclase. Por ejemplo:
CCuentaCorriente cliente01=
New CCuentaCorriente(cliente01,1234567891,10000, 3.5, 1.0, 6);
CCuentaCorrienteIn cliente;
cliente=cliente01; //error de compilacin: conversin implcita no permitida.
//es una excepcin del tipo ClassCastException, debido a que la conversin
//explicita requerida no se permite.
Cliente=(CCuentaCorrienteConIn)cliente01;
//La lnea anterior es valida si cliente01 referencia a un objeto de la clase cliente,
//esto es, CCuentaCorrienteConIn.
POLIMORFISMO.
La palabra polimorfismo significa la facultad de asumir muchas formas,
refirindose a la facultad de llamar a muchos mtodos diferentes utilizando una
nica sentencia.
Cuando se invoca un mtodo que est definido en la superclase y redefinido en sus
subclases, la versin que se ejecuta depende de la clase del objeto referenciado, no
del tipo de variable que lo referencia.
Una referencia a una subclase puede ser convertida implcitamente por Java en una
referencia a su superclase directa o indirecta. Esto significa que es posible referirse
a un objeto de una subclase utilizando una variable del tipo de su superclase
Se puede trabajar una matriz de referencias en la que cada elemento seale a un
objeto de alguna de las subclases de la jerarqua construida anteriormente. De qu
tipo deben ser los elementos de la matriz? Segn el prrafo anterior deben de ser
del la clase CCuenta; de esta forma ellos podrn almacenar indistintamente
referencias a objetos de cualquiera de las subclases. Por ejemplo:
////////////////////////////////
//////PROGRAMA 5.12
class Test6{
public static void main(String[] arg){
CCuenta[] cliente=new CCuenta[10];
cliente[0]=new CCuentaAhorro("Clien00","3000123450",10000,2.5,3.0);
cliente[1]=new CCuentaCorriente("Clien01","6000123451",10000,2.0, 1.0, 6);
cliente[2]=new CCuentaCorrienteConIn("Clien02","4000123450",10000,3.5, 1.0, 6);
122
Class CBanco
Clase CCuenta
Class CuentaAhorro
Class CuentaCorriente
Class CuentaCorrienteConIn
La estructura de datos que represente el banco tiene que ser capaz de almacenar
objetos CCuentaAhorro, CCuentaCorriente y CCuentaCorrienteConIn. Sabiendo
que cualquier referencia a un objeto de una subclase puede convertirse
implcitamente en una referencia a un objeto de su superclase, la estructura a usarse
es una matriz de referencias a la superclase CCuenta. Esta matriz ser dinmica,
esto es, aumentar en un elemento cuando se aade un objeto de alguna de las
subclases y disminuir en uno cuando se elimine. La clase CBanco, que no
pertenece a la jerarqua, tendr los siguientes atributos y mtodos:
123
ATRIBUTOS
SIGNIFICADO
cliente
nElementos
MTODOS
SIGNIFICADO
CBanco
unElementoMs
unElementoMenos
insertarCliente
clienteEn
longitud
elimina
buscar
124
clientes[i] = clientesActuales[i];
nElementos++;
}
private void unElementoMenos(CCuenta[] clientesActuales){
if (clientesActuales.length == 0) return;
int k = 0;
nElementos = clientesActuales.length;
// Crear una matriz con un elemento menos
clientes = new CCuenta[nElementos - 1];
// Copiar los clientes no nulos que hay actualmente
for ( int i = 0; i < nElementos; i++ )
if (clientesActuales[i] != null)
clientes[k++] = clientesActuales[i];
nElementos--;
}
public void insertarCliente( int i, CCuenta objeto ){
// Asignar al elemento i de la matriz, un nuevo objeto
if (i >= 0 && i < nElementos)
clientes[i] = objeto;
else
System.out.println("ndice fuera de lmites");
}
public CCuenta clienteEn( int i ){
// Devolver la referencia al objeto i de la matriz
if (i >= 0 && i < nElementos)
return clientes[i];
else{
System.out.println("ndice fuera de lmites");
return null;
}
}
public int longitud() { return nElementos; }
public void aadir(CCuenta obj){
// Aadir un objeto a la matriz
unElementoMs(clientes);
insertarCliente( nElementos - 1, obj );
}
public boolean eliminar(String cuenta){
// Buscar la cuenta y eliminar el objeto
for ( int i = 0; i < nElementos; i++ )
if (cuenta.compareTo(clientes[i].obtenerCuenta()) == 0){
clientes[i] = null; // enviar el objeto a la basura
unElementoMenos(clientes);
return true;
125
}
return false;
}
public int buscar(String str, int pos){
// Buscar un objeto y devolver su posicin
String nom, cuen;
if (str == null) return -1;
if (pos < 0) pos = 0;
for ( int i = pos; i < nElementos; i++ ){
// Buscar por el nombre
nom = clientes[i].obtenerNombre();
if (nom == null) continue;
// str est contenida en nom?
if (nom.indexOf(str) > -1)
return i;
// Buscar por la cuenta
cuen = clientes[i].obtenerCuenta();
if (cuen == null) continue;
// str est contenida en cuen?
if (cuen.indexOf(str) > -1)
return i;
}
return -1;
}
}
//////////////////////////////////////////////////////////////////
La clase CBanco, su constructor inicia la matriz clientes con cero elementos. Al
aadir un objeto (un cliente) a la matriz se hace en dos pasos; uno, incrementar a la
matriz en un elemento y dos, asignar la referencia al objeto al nuevo elemento de la
matriz; por lo que el eliminar un elemento de la matriz tambin se realiza en dos
pasos: uno, poner a null el elemento de la matriz que referencia al objeto que se
desea eliminar (el objeto se enva a la basura para ser recogido por el recolector de
basura), y dos, quitar ese elemento de la matriz decrementando su tamao en uno.
El incremento y decremento se realiza en un nuevo espacio de memoria por lo que
el espacio anterior queda sin referencia, condicin suficiente para que sea enviado a
la basura.
Una aplicacin que utilizar CBanco presentar un men como se indica a
continuacin:
1.
2.
3.
4.
5.
6.
7.
8.
Saldo.
Buscar siguiente
Ingreso
Reintegro
Aadir
Eliminar
Mantenimiento
Salir
126
127
128
if (pos == -1)
if (banco.longitud() != 0)
flujoS.println("bsqueda fallida");
else
flujoS.println("no hay clientes");
else{
flujoS.println(banco.clienteEn(pos).obtenerNombre());
flujoS.println(banco.clienteEn(pos).obtenerCuenta());
flujoS.println(banco.clienteEn(pos).estado());
}
break;
case 3: // ingreso
case 4: // reintegro
flujoS.print("Cuenta: "); cuenta = Leer.dato();
pos = banco.buscar(cuenta, 0);
if (pos == -1)
if (banco.longitud() != 0)
flujoS.println("bsqueda fallida");
else
flujoS.println("no hay clientes");
else{
flujoS.print("Cantidad: "); cantidad = Leer.datoDouble();
if (opcin == 3)
banco.clienteEn(pos).ingreso(cantidad);
else
banco.clienteEn(pos).reintegro(cantidad);
}
break;
case 5: // aadir
flujoS.print("Tipo de cuenta: 1-(CA), 2-(CC), 3-CCI ");
do
opcin = Leer.datoInt();
while (opcin < 1 || opcin > 3);
banco.aadir(leerDatos(opcin));
break;
case 6: // eliminar
flujoS.print("Cuenta: "); cuenta = Leer.dato();
eliminado = banco.eliminar(cuenta);
if (eliminado)
flujoS.println("registro eliminado");
else
if (banco.longitud() != 0)
flujoS.println("cuenta no encontrada");
else
flujoS.println("no hay clientes");
break;
case 7: // mantenimiento
for (pos = 0; pos < banco.longitud(); pos++){
banco.clienteEn(pos).comisiones();
banco.clienteEn(pos).intereses();
129
}
break;
case 8: // salir
banco = null;
}
}
while(opcin != 8);
}
}
/////////////////////////////////////////////////////////////////
Se observa que el mantenimiento de las cuentas de los clientes (case 7) resulta
sencillo gracias a la aplicacin de la definicin de polimorfismo. Esto es, el mtodo
comisiones o intereses que se invoca para cada cliente depende del tipo del objeto
referenciado por el elemento accedido de la matriz clientes de banco.
MTODOS EN LINEA.
Cuando el compilador Java conoce con exactitud qu mtodo tiene que llamar para
responde al mensaje que se ha programado que un objeto reciba en un instante
determinado, puede tomar la iniciativa de reemplazar la llamada al mtodo por el
cuerpo del mismo. Se dice entonces que el mtodo est en lnea. Esto redunda en
tiempos de ejecucin ms cortos.
Un mtodo no es en lnea cuando el compilador no sepa con exactitud a qu
versin del mtodo tiene que invocar. Por ejemplo, si se tiene una matriz de
referencia a objetos de las subclases CCuentaAhorro, CCuentaCorriente o
CCuentaCorrienteConIn, cmo sabe el compilador a qu mtodo interes tiene que
llamar?. Cuando sucede esto, el compilador produce cdigo que permite al
imterprete Java ejecutar el mtodo adecuado.
Esta forma dinmica de ejecutar mtodos es ms lenta que los mtodos en lnea.
Afortunadamente existen pocos mtodos que no son del tipo lnea. Los mtodos
del tipo final, static y private pueden ser invocados en forma directa.
INTERFACES.
Es un dispositivo o sistema utilizado por entidades inconexas para interactuar. Bajo
esta definicin, una interfaz Java es un dispositivo que permite interactuar a objetos
no relacionados entre s. Son un conjunto de mensajes que se pueden aplicar a
muchas clases de objetos, a los que cada una de ellas debe responder de forma
adecuada. Por eso una interfaz tambin recibe el nombre de protocolo.
Una interfaz en Java consta de dos partes: el nombre de la interfaz precedido por la
palabra reservada interface, y el cuerpo de la interfaz encerrado entre llaves:
[public] interface nombre_interfaz extends superinterfaces {
cuerpo de la interfaz
}
El modificador public indica que la interfaz puede ser utilizada por cualquier clase
de cualquier paquete. Si no se especifica, entonces slo estar accesible para las
clases definidas en el mismo paquete de la interfaz.
130
GregorianCalendar
Mtodo get
IFecha
Mtodo
da
Subclase de
CCUenta
Mtodo da
131
Una interfaz slo declara los mtodos, no los define, y adems puede definir
constantes. Todo mtodo declarado en una interfaz es implcitamente publico y
abstracto (public y abstract); y todas las constantes son implcitamente pblicas,
finales y estticas (public, final y static). Cualquier clase puede tener acceso a las
constantes de la interfaz a travs del nombre de la misma. Por ejemplo:
IFecha.AO
En cambio, una clase que implemente la interfaz puede tratar las constantes como
si las hubiesen heredado; esto es, accediendo directamente a su nombre.
Para utilizar una interfaz hay que aadir el nombre de la misma precedido por la
palabra clave implements a la definicin de la clase. La palabra clave implements
sigue a la palabra clave extends, si existe.
La subclase CCuentaAhorro que utilice la interfaz IFecha debe definirse as:
//////////////////////////////////////////////////////////////////
/////PROGRAMA 5.16
//////////////////////////////////////////////////////////////////
// Clase CCuentaAhorro: clase derivada de CCuenta
/////////////////////////////////////////////////////////////////
import java.util.*;
public class CCuentaAhorro extends CCuenta implements IFecha{
// Atributos
private double cuotaMantenimiento;
// Mtodos
public CCuentaAhorro() {} // constructor sin parmetros
public CCuentaAhorro(String nom, String cue, double sal,
double tipo, double mant){
super(nom, cue, sal, tipo); // invoca al constructor CCuenta
asignarCuotaManten(mant); // inicia cuotaMantenimiento
}
public void asignarCuotaManten(double cantidad){
if (cantidad < 0){
System.out.println("Error: cantidad negativa");
return;
}
cuotaMantenimiento = cantidad;
}
public double obtenerCuotaManten(){
return cuotaMantenimiento;
}
132
133
134
135
Clase Object
Class CBanco
Clase CCuenta
Class CuentaAhorro
Class CuentaCorriente
Interfaz IFecha
Class CuentaCorrienteConIn
Interfaz Ixxx
Clase1
Clase2
Clase3
137
138
CLASES ANIDADAS.
Una clase anidada es una clase que es un miembro de otra clase. Se debe definir
dentro de otra slo cuando tenga sentido en el contexto de la clase que la incluye o
cuando depende de la funcin que desempea la clase que la incluye. Por ejemplo,
una ventana puede definir su propio cursor; en este caso, la ventana puede ser un
objeto de una clase y el cursor de una clase anidada.
La clase anidada es un miembro ms de la clase que la contiene. La clase anidada
puede ser pblica, privada, protegida o esttica.
Cuando un miembro de una clase es una clase anidada y no es del tipo static recibe
el nombre de clase interna por tratarse de un miembro del objeto. Esto significa
que cuando se cree un objeto de la clase externa tambin se crear uno de la
interna.
Una clase interna que est asociada con un objeto, lgicamente no puede tener
miembros del tipo static.
Un objeto de una clase interna puede existir slo dentro de un objeto de su clase
externa. Como se trata de un miembro de clase externa, tiene acceso directo al resto
de los miembros de esa clase. Por ejemplo:
public class CPersona
{
private String nombre;
private CFecha fechaNacimiento;
private class CFecha
{
private int da, mes, ao;
private CFecha(int dd, int mm, int aa)
{
da = dd; mes = mm; ao = aa;
}
}
public CPersona() {}
public CPersona(String nom, int dd, int mm, int aa)
{
nombre = nom;
fechaNacimiento = new CFecha(dd, mm, aa);
}
public String obtenerNombre() { return nombre; }
public String obtenerFechaNa()
{
return fechaNacimiento.da + "/" +
fechaNacimiento.mes + "/" +
fechaNacimiento.ao;
}
}
139
140
Clases annimas.
Algunas veces podemos escribir una clase dentro de un mtodo sin necesidad de
identificarla. Una clase definida de esta forma recibe el nombre de clase annima.
Por otra parte, para crear con new un objeto de una clase necesitamos conocer en
nombre de la misma. Entoces para qu utilizar clases annimas?
interface Interfaz{
public abstract void p();
public abstract void m();
}
class Clase2{
public Interfaz metClase2(){
return new Clase3();
}
class Clase3 implements Interfaz{
public void p(){
System.out.println("Estoy en el mtodo P");
}
public void m(){
System.out.println("Estoy en el mtodo M");
}
}
}
public class Clase1{
public static void main(String[] args){
Clase2 obj=new Clase2();
Interfaz iobj=obj.metClase2();
iobj.m();
}
}
En este ejemplo se puede observar que la Clase2 define una clase interna Clase3 y
un mtodo metClase2 que devuelve una referencia del tipo Interfaz a un objeto
Clase3 que implementa dicha interfaz. El mtodo main de Clase1 pone a prueba la
capacidad de Clase2.
Una alternativa al ejemplo planteado es definir annima la clase utilizada dentro
del mtodo (en el ejemplo Clase3). Por ser annima, su definicin debera ocupar
el lugar donde se utiliza su nombre para crear un objeto de la misma. Esto es, la
definicin y la creacin del objeto se haran bajo la siguiente sintaxis:
New Ixxx(){. . .}
Donde Ixxx es el nombre de la interfaz que implementa la clase. Por ejemplo:
141
interface Interfaz{
public abstract void p();
public abstract void m();
}
class Clase2{
public Interfaz metClase2(){
return new Interfaz(){
public void p(){
System.out.println("Estoy en el mtodo P");
}
public void m(){
System.out.println("Estoy en el mtodo M");
}
};
}
}
public class Clase1{
public static void main(String[] args){
Clase2 obj=new Clase2();
Interfaz iobj=obj.metClase2();
iobj.m();
}
}
Se puede observar que la llamada a new va seguida de la definicin de la clase y
que no se utiliza el nombre de la clase, sino el nombre de la interfaz que la clase
implementa; notar que en este caso no interviene la palabra clave implements y
que la sentencia return finaliza con un punto y coma.
Resumiendo, una clase annima es una forma de evitar el que tengamos que pensar
en un nombre trivial para una clase pequea en cdigo, utilizada en un lugar
concreto. Evidentemente, el precio que se paga es que no podremos crear objetos
de esta clase fuera del lugar donde haya sido definida.
Ejercicio.
Las clases subsecuentes permiten sumar dos polinomios y se construye otro tercer
polinomio que contenga los trminos de los otros dos, pero sumando los
coeficientes que se repitan en ambos.
La aplicacin solicita los coeficientes del polinomio pA y sus exponentes de x y
de y , para detener el proceso se da como valores en el coeficiente y en los dos
exponentes el valor de cero.
Explique, mtodo por mtodo la forma en que funcionan las clases Test,
CPolinomio y CTermino.
142
import java.math.*;
//////////////////////////////////////////////////////////////////
// Clase CTermino: expresin de la forma a.x^n.y^m
//
a es el coeficiente de tipo double.
//
n y m son los exponentes enteros de x e y.
//
public class CTermino{
private double coeficiente = 0.0; // coeficiente
private int exponenteDeX = 1; // exponente de x
private int exponenteDeY = 1; // exponente de y
public CTermino() {}
public CTermino( double coef, int expx, int expy ) // constructor
{
coeficiente = coef;
exponenteDeX = expx;
exponenteDeY = expy;
}
public CTermino(CTermino t) // constructor copia
{
coeficiente = t.coeficiente;
exponenteDeX = t.exponenteDeX;
exponenteDeY = t.exponenteDeY;
}
public CTermino copiar(CTermino t) // asignacin
{
coeficiente = t.coeficiente;
exponenteDeX = t.exponenteDeX;
exponenteDeY = t.exponenteDeY;
return this;
}
public void asignarCoeficiente(double coef) {coeficiente = coef;}
public double obtenerCoeficiente() {return coeficiente;}
public void asignarExponenteDeX(int expx) {exponenteDeX = expx;}
public int obtenerExponenteDeX() {return exponenteDeX;}
public void asignarExponenteDeY(int expy) {exponenteDeY = expy;}
public int obtenerExponenteDeY() {return exponenteDeY;}
public void mostrarTermino()
{
if (coeficiente == 0) return;
// Signo
String sterm = (coeficiente < 0) ? " - " : " + ";
// Coeficiente
if (Math.abs(coeficiente) != 1)
sterm = sterm + Math.abs(coeficiente);
// Potencia de x
if (exponenteDeX > 1 || exponenteDeX < 0)
sterm = sterm + "x^" + exponenteDeX;
else if (exponenteDeX == 1)
sterm = sterm + "x";
143
// Potencia de y
if (exponenteDeY > 1 || exponenteDeY < 0)
sterm = sterm + "y^" + exponenteDeY;
else if (exponenteDeY == 1)
sterm = sterm + "y";
// Mostrar trmino
System.out.print(sterm);
}
}
//////////////////////////////////////////////////////////////////
import java.math.*;
//////////////////////////////////////////////////////////////////
// Clase CPolinomio. Un objeto CPolinomio consta de uno o ms
//
objetos CTermino.
//
public class CPolinomio{
private CTermino[] trminos; // matriz de objetos
private int nElementos; // nmero de elementos de la matriz
public CPolinomio(){
// Crear una matriz vaca
nElementos = 0;
trminos = new CTermino[nElementos];
}
private void unElementoMs(CTermino[] trminosAct){
nElementos = trminosAct.length;
// Crear una matriz con un elemento ms
trminos = new CTermino[nElementos + 1];
// Copiar los trminos que hay actualmente
for ( int i = 0; i < nElementos; i++ )
trminos[i] = trminosAct[i];
nElementos++;
}
private void unElementoMenos(CTermino[] trminosAct){
if (trminosAct.length == 0) return;
int k = 0;
nElementos = trminosAct.length;
// Crear una matriz con un elementos menos
trminos = new CTermino[nElementos - 1];
// Copiar los trminos no nulos que hay actualmente
for ( int i = 0; i < nElementos; i++ )
if (trminosAct[i] != null)
trminos[k++] = trminosAct[i];
nElementos--;
}
144
145
146
}
// Trminos restantes en el pA
while ( ipa < na ){
coefA = trminos[ipa].obtenerCoeficiente();
expXA = trminos[ipa].obtenerExponenteDeX();
expYA = trminos[ipa].obtenerExponenteDeY();
pR.insertarTermino(new CTermino(coefA, expXA, expYA));
ipa++;
}
// Trminos restantes en el pB
while ( ipb < nb ){
coefB = pB.trminos[ipb].obtenerCoeficiente();
expXB = pB.trminos[ipb].obtenerExponenteDeX();
expYB = pB.trminos[ipb].obtenerExponenteDeY();
pR.insertarTermino(new CTermino(coefB, expXB, expYB));
ipb++;
}
// Quitar los trminos con coeficiente 0
k = 0;
while ( k < pR.nElementos ){
if (pR.trminos[k].obtenerCoeficiente() == 0){
pR.eliminarTermino(k);
pR.nElementos--;
}
else
k++;
}
return pR;
}
public void mostrarPolinomio(){
int i = nElementos;
while ( i-- != 0 )
trminos[i].mostrarTermino();
}
public double valorPolonomio(double x, double y){
double v = 0;
for ( int i = 0; i < nElementos; i++ )
v += trminos[i].obtenerCoeficiente() *
Math.pow(x, trminos[i].obtenerExponenteDeX()) *
Math.pow(y, trminos[i].obtenerExponenteDeY());
return v;
}
}
//////////////////////////////////////////////////////////////////
147
148
149