You are on page 1of 46

APUNTES DE JAVA

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

Esta jerarqua se puede analizar desde dos puntos de vista:


1. En el inicio, el diseo de una aplicacin para administrar las cuentas de una
entidad bancaria, fue suficiente con la capacidad proporcionada por la clase
CCuenta, Al evolucionar el mercado bancario, surgieron nuevas
modalidades de cuentas. La mejor solucin fue definir una subclase para
cada modalidad ya que el mecanismo de herencia pona a disposicin de las
subclases todo el cdigo de la superclase, al que slo era necesario aadir
nuevas especificaciones. Evidentemente, la herencia es una forma sencilla
de reutilizar cdigo proporcionado por otras clases.
2. Cuando se abord el diseo de una aplicacin para administrar las cuentas
de una entidad bancaria, la solucin fue disear una clase especializada para
cada una de las cuentas y posteriormente agrupar el cdigo comn en una
superclase de estas (diseo ascendente).

104

En ambos casos, la herencia es la solucin para reutilizar cdigo.


Para disear CCuenta, se observan los atributos y mtodos comunes a cualquier
tipo de cuenta:
Atributos

Significado

nombre
cuenta
saldo
tipoDeInteres

Dato de tipo String que almacena el nombre del propietario.


Dato de tipo String que almacena el nmero de cuenta
Dato de tipo double que almacena el saldo de la cuenta.
Dato de la clase de tipo double que almacena el tipo de
interes.

Mtodo

Significado

CCuenta

El constructor de la clase. Inicia los datos nombre, cuenta,


saldo y tipoDeInteres.
aignarNombre
Permite asignar el dato nombre.
obtenerNombre
Retorna el dato nombre.
asignarCuenta
Permite asignar el dato cuenta.
obtenerCuenta
Retorna el dato cuenta.
estado
Retorna el saldo de la cuenta.
comisiones
Es un mtodo abstracto sin parmetros que ser redefinido en
las subclases. Se ejecutar los das uno de cada mes para
cobrar el importe del mantenimiento de una cuenta.
ingreso
Es un mtodo que tiene un parmetro cantidad tipo doble
que aade la cantidad especificada al saldo actual de la
cuenta.
reintegro
Es un mtodo que tiene un parmetro cantidad de tipo
doble que resta la cantidad especificada del saldo actual de
la cuenta.
asignarTipoDeInteres Mtodo que permite asignar el dato tipoDe Interes
obtenerTipoDeInteres Mtodo que retorna el dato tipoDeInteres
interes
Mtodo abstracto. Calcula los intereses producidos.
El cdigo es el siguiente:
//////////////////////////////////////////////////////////////////
//
PROGRAMA 5.1
// Clase CCuenta: clase abstracta que agrupa los datos comunes a
// cualquier tipo de cuenta bancaria.
//
public abstract class CCuenta{
// Atributos
private String nombre;
private String cuenta;
private double saldo;
private double tipoDeInters;

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

if (saldo - cantidad < 0){


System.out.println("Error: no dispone de saldo");
return;
}
saldo -= cantidad;
}
public void asignarTipoDeInters(double tipo){
if (tipo < 0){
System.out.println("Error: tipo no vlido");
return;
}
tipoDeInters = tipo;
}
public double obtenerTipoDeInters(){
return tipoDeInters;
}
}
// fin de CCuenta
Si se intenta crear un objeto del tipo CCuenta:
CCuenta cliente01=new CCuenta();
Se obtendr un error indicando que la clase es abstracta.
DEFINIR UNA SUBCLASE.
Ahora bien, una cuenta de ahorro tiene las caractersticas aportadas por un objeto
del tipo CCuenta, y adems algunas otras; por ejemplo, un atributo que especifique
el importe que hay que pagar mensualmente por el mantenimiento de la misma.
Esto significa que se tiene que disear una nueva clase, CCuentaAhorro, que tenga
las mismas capacidades de CCuenta, pero a las que se tienen que aadir otras que
den solucin a las nuevas necesidades.
A travs de la herencia, Java permite definir la clase CCuentaAhorro como una
extensin de CCuenta.
La sintaxis para definir una subclase es la siguiente:
class nombre_subclase extends nombre_superclase{
//cuerpo de la subclase.
}
La palabra clave extends significa que se est definiendo una clase denominada
nombre_subclase que es una extensin de otra denominada nombre_superclase.
Si no se especifica la clasula extends con el nombre de la superclase, se
entiende que la superclase es la clase Object.
Java, a diferencia de otros lenguajes O. O. no permite la herencia mltiple.
Una subclase puede a su vez, ser una superclase de otra, dando lugar as a una
jerarqua de clases. Una superclase puede ser directa de una subclase, si figura
explcitamente en la definicin de la subclase, o puede ser indirecta si est varios
niveles arriba en la jerarqua de clases y por lo tanto no figura explcitamente en el
encabezado de la subclase.

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.

Puede Se Accedido Desde.

Un Miembro Declarado En Una Clase Como


Privado
predeterminado protegido publico

Su misma clase

Si

Si

Si

Si

Cualquier clase o subclase


del mismo paquete
Cualquier clase de otro
paquete
Cualquier subclase de otro
paquete

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

Mtodo que se ejecuta los das uno de cada mes para


cobrar el importe correspondiente al mantenimiento
de la cuenta.
Mtodo que permite calcular el importe
correspondiente a los intereses/mes producidos

La definicin correspondiente a esta clase se expone a continuacin:


import java.util.*;
//////////////////////////////////////////////////////////////////
//
PROGRAMA 5.2
// Clase CCuentaAhorro: clase derivada de CCuenta
//
public class CCuentaAhorro extends CCuenta{
// 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;
}
public void comisiones(){
// Se aplican mensualmente por el mantenimiento de la cuenta
GregorianCalendar fechaActual = new GregorianCalendar();
int da = fechaActual.get(Calendar.DAY_OF_MONTH);
if (da == 1) reintegro(cuotaMantenimiento);
}
public double intereses(){
GregorianCalendar fechaActual = new GregorianCalendar();
int da = fechaActual.get(Calendar.DAY_OF_MONTH);
if (da != 1) return 0.0;

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

miembros privados. Si las clases CCuentaAhorro y CCuenta pertenecen a otro


paquete, la clase Test solo tendra acceso a los miembros pblicos.
Los mtodo de una subclase no tiene acceso a los miembros privados de su
superclase, pero s lo tiene a sus miembros protegidos y pblicos, y si la subclase
pertenece al mismo paquete que la superclase, tambin tiene acceso a sus miembros
predeterminados. Por ejemplo, el mtodo comisiones de la clase CuentaAhorro no
puede acceder al atributo saldo de la clase CCuenta porque es privado, pero s
puede acceder a su mtodo pblico reintegro. Esta restriccin permite imponer la
encapsulacin. Si una subclase tuviera acceso a los miembros privados de su
superclase, entonces cualquiera podra acceder a los miembros privados de una
clase, simplemente derivando una clase de ella. Consecuentemente, si una subclase
quiere acceder a los miembros privados de su subclase, debe hacerlo a travs de la
interfaz pblica, protegida, o predeterminada en su caso, de dicha superclase.
ATRIBUTOS CON EL MISMO NOMBRE.
Qu sucede si definimos en la subclase uno de sus atributos con el mismo nombre
que tiene en la superclase? Supongamos que una ClaseA define un atributo
identificado por atributo_x, que despus redefinimos en la subclase ClaseB:
///////////////////////////////////////////////////////
//
PROGRAMA 5.4
class ClaseA{
public int atributo_x = 1;
public int mtodo_x(){
System.out.println("Estoy en mtodo_x de la ClaseA donde: return atributo_x*10");

return atributo_x * 10;


}
public int mtodo_y(){
System.out.println("Estoy en mtodo_y de la ClaseA donde: return atributo_x+100");

return atributo_x + 100;


}
protected void finalize() throws Throwable // destructor
{
System.out.println("Recursos de ClaseA liberados");
}
}//fin de ClaseA

class ClaseB extends ClaseA{


protected int atributo_x = 2;
public int Retorna(){
return atributo_x;
}
public int Retorna1(){
return super.atributo_x;
111

}
public int mtodo_x() {
System.out.println("Estoy en mtodo_x de la ClaseB donde: return atributo_x*-10");

return atributo_x * -10;


}
public int mtodo_z(){
System.out.println("En mtodo_z, atributo_x en clase A es="+Retorna1());
System.out.println("En mtodo_z, atributo_x en clase B es="+Retorna());
System.out.println("Mtodo_z de la ClaseB donde:");
System.out.println("atruboto_x=super.atributo_x+3 ");
System.out.println("return super.mtodo_x() + atributo_x");
atributo_x = super.atributo_x + 3;
return super.mtodo_x() + atributo_x;
}
protected void finalize() throws Throwable // destructor
{
System.out.println("Recursos de ClaseB liberados");
super.finalize();
}
}
public class Test1
{
public static void main(String[] args)
{
ClaseB objClaseB = new ClaseB();
System.out.println("Valor de atributo_x en ClaseA = " +
objClaseB.Retorna1());
System.out.println("Valor de atributo_x en ClaseB="+objClaseB.Retorna());
System.out.println("En el main: "+objClaseB.mtodo_y()); // escribe 101
System.out.println("En el main: "+objClaseB.mtodo_x()); // escribe -20
System.out.println("En el main: "+objClaseB.mtodo_z()); // escribe 14
objClaseB = null;
// Ejecutar el recolector de basura
Runtime runtime = Runtime.getRuntime();
runtime.gc();
runtime.runFinalization();
}
}
En este caso, la definicin del atributo_x de la subclase oculta la definicin del
atributo con el mismo nombre de la superclase. Si el valor a acceder es atributo_x
de la ClaseA se tiene la facilidad de acceso utilizando la palabra reservada super.
public int mtodo_x(){
return super.atributo_x*-10;
}

112

Como se puede ver, podemos referirnos al dato atributo_x de la superclase con la


expresin:
super.atributo_x
Asimismo, podramos referirnos al dato atributo_x de la subclase con la expresin:
this.atributo_x
Tambin podramos referirnos al atributo_x de la ClaseA de la siguiente forma:
((ClaseA)this).atributo_x
Esta es la tcnica que se requiere para hacer una conversin explcita obligada si
necesitamos referirnos a un miembro oculto perteneciente a una clase por encima
de la superclase (una clase base indirecta).
Cuando se invoca a un mtodo en respuesta a un mensaje recibido por un objeto,
Java busca su definicin en la clase del objeto. La definicin del mtodo que all se
encuentra puede pertenecer a la propia clase o haber sido heredada de alguna
superclase. Si Java no encuentra el mtodo en la clase del objeto, la buscar hacia
arriba a las superclases hasta localizarla.
Puede haber ocasiones en que deseemos que un objeto de una subclase responda al
mismo mtodo heredado de su superclase pero con un comportamiento diferente.
Esto implica redefinir en la subclase el mtodo heredado de su superclase.
Redefinir un mtodo heredado significa volver a escribir en la subclase con el
mismo nombre, la misma lista de parmetros y el mismo tipo del valor retornado
que tena la superclase. Esto es lo que hace el mtodo_x del ejemplo expuesto en
el apartado anterior.
Cuando en una subclase se redefine un mtodo de una superclase, se oculta el
mtodo de la superclase. Si el mtodo se redefine en la subclase con distinto tipo o
nmero de parmetros, el mtodo de la superclase no se oculta, sino que se
comporta como una sobrecarga del mtodo.
En caso de que el mtodo heredado por la subclase sea abstracto, como sucede en
el ejemplo de las cuentas bancarias, es obligatorio redefinirlo de lo contrario la
subclase debera ser declarada tambin abstracta.
Como regla, no se puede redefinir un mtodo en una subclase y hacer que su
control de acceso sea ms restrictivo que el original. El orden de los tipos de
control de acceso de ms a menos restrictivo es as: private, predeterminado,
protected y public. Por lo que, un mtodo que sea protected en la superclase,
podr ser redefinido comoprotected o public; si es public slo podr ser
redefinido como public. Si es private no tiene sentido hablar de redefinicin
porque no puede ser accedido nada ms que desde su propia clase.
Para acceder a un mtodo de la superclase que ha sido redefinido en la subclase se
utilizar la palabra reservada super. Por ejemplo:
super.mtodo_x()
Para redefinir al mtodo_x de la subclase, se puede usar la siguiente sintaxis:
this.mtodo_x()
No es posible usar la siguiente expresin:
((ClaseA)this).mtodo_x()
Para referirse al mtodo_x() de la ClaseA desde la ClaseB ya que en Java el
mtodo invocado, cuando se trate de un mtodo redefinido, siempre pertenece a la
clase del objeto no de la referencia; por lo tanto, el mtodo_x invocado ser el de la
ClaseB.

113

CONSTRUCTORES DE LAS SUBCLASES.


Cuando se tiene una relacin subclase-superclase, al momento de construir un
objeto de una subclase, se invoca a su constructor, que a su vez invoca al
constructor sin parmetros de la superclase, que a su vez invoca al constructor de
su superclase, y as sucesivamente.
Ahora bien, si se han definido constructores con parmetros tanto en subclases
como en superclases, tal vez se desee construir un objeto de la subclase inicindolo
con unos valores predeterminados. Esto permite que el constructor de subclase
invoque explcitamente al constructor de la superclase. Esto se hace utilizando la
palabra reservada super.
Nombre_subclase(lista de parmetros){
Super(lista de parmetros);
//cuerpo del constructor de la subclase.
}
Cuando desde un constructor de una subclase se invoca al constructor de su
superclase, esta lnea tiene que ser la primera.
La sintaxis y los requerimientos son anlogos a los utilizados con this cuando se
llama a otro constructor de la misma clase.
Si la superclase no tiene un constructor en forma explicita o tiene uno que no
requiere parmetros, no se necesita invocarlo explcitamente, ya que Java lo
invocar automticamente mediante super() sin argumentos.
Por ejemplo, en la clase CCuentaAhorro se tiene el constructor:
super(nom, cue, sal, tipo);
Esta linea llama al constructor de CCuenta, superclase de CCuentaAhorro. Por lo
que CCuenta debe tener un constructor con cuatro parmetros del tipo de los
argumentos especificados.
De acuerdo con estos mtodos constructores definidos en la clase CCuentaAhorro,
son declaraciones validas las siguientes:
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);

//. . .
}
}
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

DESTRUCTORES DE LAS SUBCLASE.


El constructor de una subclase invoca automticamente el constructor sin
parmetros de su superclase. En un destructor no ocurre as.
Si definimos en una subclase un mtodo finalize para liberar recursos asignados
por dicha clase, debemos redefinir el mtodo finalize en la superclase para liberar
recursos asignados por ella. Si el mtodo finalize de la subclase no invoca
explcitamente al mtodo finalize de la superclase, este ltimo nunca ser
liberado. El mejor lugar para invocar el mtodo finalize es la ultima lnea del
mtodo finalize de la subclase ya que el orden de destruccin debe ser inverso al
orden de construccin.
El ejemplo de destructores se encuentra en las clases ClaseA y ClaseB, y aadimos
un destructor a cada una de ellas.
Observe que en el mtodo main de la clase Test1 (Fig. 5.4), se tiene el siguiente
cdigo:
System.out.println("En el main: "+objClaseB.mtodo_z()); // escribe 14
objClaseB = null; // Ejecutar el recolector de basura
Runtime runtime = Runtime.getRuntime();
runtime.gc();
runtime.runFinalization();
}
Cuando se realiza la asignacin objClaseB=null, lo que realmente est pasando es
enviar al objeto que est referenciando objClaseB a la basura. Por lo que se
provocar la ejecucin de los dos destructores: primero ClaseB y despus la clase
ClaseA.
JERARQUIA DE CLASES.
Una subclase puede ser asimismo una superclase de otra clase, y as sucesivamente.
En la siguiente figura se puede ver esto con claridad:
Clase Object

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

Por lo que la definicin de la clase CCuentaCorriente es:


/////////////////////////////////////////////
/////PROGRAMA 5.5
import java.util.*;
//////////////////////////////////////////////////////////////////
// Clase CCuentaCorriente: clase derivada de CCuenta
//
//////////////////////////////////////////////////////////////////
public class CCuentaCorriente extends CCuenta{
// Atributos
private int transacciones;
private double importePorTrans;
private int transExentas;
// Mtodos
public CCuentaCorriente() {} // constructor sin parmetros
public CCuentaCorriente(String nom, String cue, double sal,
double tipo, double imptrans, int transex){

116

super(nom, cue, sal, tipo);


//
transacciones = 0;
asignarImportePorTrans(imptrans);
asignarTransExentas(transex);

invoca al
// inicia
// inicia
// inicia

constructor CCuenta
transacciones
importePorTrans
transExentas

public void decrementarTransacciones(){


transacciones--;
}
public void asignarImportePorTrans(double imptrans){
if (imptrans < 0)
{
System.out.println("Error: cantidad negativa");
return;
}
importePorTrans = imptrans;
}
public double obtenerImportePorTrans(){
return importePorTrans;
}
public void asignarTransExentas(int transex){
if (transex < 0){
System.out.println("Error: cantidad negativa");
return;
}
transExentas = transex;
}
public int obtenerTransExentas(){
return transExentas;
}
public void ingreso(double cantidad){
super.ingreso(cantidad);
transacciones++;
}
public void reintegro(double cantidad){
super.reintegro(cantidad);
transacciones++;
}
public void comisiones(){
// Se aplican mensualmente por el mantenimiento de la cuenta
GregorianCalendar fechaActual = new GregorianCalendar();
int da = fechaActual.get(Calendar.DAY_OF_MONTH);
if (da == 1){
int n = transacciones - transExentas;
if (n > 0) reintegro(n * importePorTrans);
transacciones = 0;
}
}
public double intereses(){
GregorianCalendar fechaActual = new GregorianCalendar();
int da = fechaActual.get(Calendar.DAY_OF_MONTH);
if (da != 1) return 0.0;

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();

// Devolver el inters mensual por si fuera necesario


return interesesProducidos;

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.

La definicin de esta clase es:


//////////////////////////////////////
/////PROGRAMA 5.6
//////////////////////////////////////////////////////////////////
// Clase CCuentaCorrienteConIn: clase derivada de CCuentaCorriente
//
//////////////////////////////////////////////////////////////////
import java.util.*;
public class CCuentaCorrienteConIn extends CCuentaCorriente {
// Mtodos
public CCuentaCorrienteConIn() {} // constructor sin parmetros
public CCuentaCorrienteConIn(String nom, String cue, double sal,
double tipo, double imptrans, int transex)
{
// Invocar al constructor de la superclase
super(nom, cue, sal, tipo, imptrans, transex);
}
public double intereses(){
GregorianCalendar fechaActual = new GregorianCalendar();
int da = fechaActual.get(Calendar.DAY_OF_MONTH);
if (da != 1 || estado() < 3000) return 0.0;
// Acumular inters mensual slo los das 1 de cada mes
double interesesProducidos = 0.0;
interesesProducidos= estado() * obtenerTipoDeInters()/ 1200.0;

118

ingreso(interesesProducidos);
// Este ingreso no debe incrementar las transacciones
decrementarTransacciones();

// Devolver el inters mensual por si fuera necesario


return interesesProducidos;

La clase CCuenta es la superclase directa de CCuentaAhorro y de


CCuentaCorriente, y es una superclase indirecta para CCuentaCorrienteConIn.
Lo que una subclase hereda de su superclase, ser heredado a su vez por una
subclase de ella, y as sucesivamente.
Una subclase que redefine un mtodo heredado slo tiene acceso a su propia
versin y a la publicada por su superclase directa. Por ejemplo, las clases CCuenta
y CCuentaCorrienteConIn incluyen cada una su versin del mtodo ingreso.
CCuentaCorrienteConIn, adems de su propia versin del mtodo ingreso puede
acceder a la versin de su superclase directa por medio de la palabra super, pero
no puede acceder a la versin de su superclase indirecta CCuenta (super.super es
una expresin no admitida por el compilador de Java).
A continuacin se presenta una aplicacin con algunos ejemplos de operaciones
con objetos de las clases pertenecientes a la jerarqua construida:
/////////////////////////////////////////////////////////////////
////////PROGRAMA 5.7
/////////////////////////////////////////////////////////////////
// Aplicacin para trabajar con CCuenta...
//
public class Test2{
public static void main(String[] args){
CCuentaAhorro cliente01 = new CCuentaAhorro(
"Un nombre", "Una cuenta", 10000, 3.5, 30);
System.out.println(cliente01.obtenerNombre());
System.out.println(cliente01.obtenerCuenta());
System.out.println(cliente01.estado());
System.out.println(cliente01.obtenerTipoDeInters());
System.out.println(cliente01.intereses());
CCuentaCorrienteConIn cliente02 = new CCuentaCorrienteConIn();
cliente02.asignarNombre("cliente 02");
cliente02.asignarCuenta("1234567890");
cliente02.asignarTipoDeInters(3.0);
cliente02.asignarTransExentas(0);
cliente02.asignarImportePorTrans(1.0);
cliente02.ingreso(20000);
cliente02.reintegro(10000);
cliente02.intereses();
cliente02.comisiones();
System.out.println(cliente02.obtenerNombre());
System.out.println(cliente02.obtenerCuenta());
System.out.println(cliente02.estado());
}
119

}
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

for (int i=0; cliente[i]!=null;i++){


System.out.print(cliente[i].obtenerNombre()+" ");
System.out.println(cliente[i].intereses());
}
}
}
Este ejemplo define una matriz cliente de tipo CCuenta con 10 elementos que Java
inicia con el valor null. Despus crea un objeto de una de las subclases y almacena
su referencia en el primer elemento de la matriz; aqu Java realizar una conversin
implcita del tipo de la referencia devuelta por new al tipo CCuenta. Este proceso
se repetir para cada objeto nuevo que deseemos crear (en nuestro caso tres veces).
Finalmente, utilizando un bucle mostramos el nombre del cliente y los intereses
que le correspondern por mes. Pregunta: en cul de las dos lneas de este bucle se
aplica la definicin de polimorfismo? Lgicamente en la ltima porque invoca a las
distintas definiciones del mtodo intereses utilizando el mismo medio de acceso:
una referencia a CCuenta.
Como ejemplo, se escribir un programa que cree un objeto que represente a una
entidad bancaria con un cierto nmero de clientes. El objeto ser de la clase
CBanco y los clientes sern objetos de alguna de las clases de la siguiente jerarqua
construida con los apartados anteriores
Clase Object

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

Matriz de referencia de tipo CCuenta


Nmero de elementos de la matriz.

MTODOS

SIGNIFICADO

CBanco

Constructor de la clase. Inicia la matriz clientes


con cero elementos
Aade un elemento vaco (null) al final de la
matriz incrementando su valor en uno
Elimina un elemento cuyo valor sea null,
decrementando su longitud en uno.
Asigna un objeto de alguna de las subclases de
CCuenta al elemento i de la matriz cliente.
Devuelve el objeto que est en la posicin i de la
matriz clientes.
Devuelve la longitud de la matriz.
Elimina el objeto que coincida con el nmero de
cuenta pasado como argumento, poniendo el
elemento correspondiente de la matriz a valor null.
Devuelve la posicin en la matriz clientes del
objeto cuyo nombre o cuenta total o parcial, coincida
con el valor pasado como argumento.

unElementoMs
unElementoMenos
insertarCliente
clienteEn
longitud
elimina
buscar

La definicin a esta clase se expone a continuacin:


//////////////////////////////////////////////////////////////////
///
PROGRAMA 5.13
// Clase CBanco: clase que mantiene una matriz de referencias a
// objetos de cualquier tipo de cuenta bancaria.
//
public class CBanco{
private CCuenta[] clientes; // matriz de objetos
private int nElementos; // nmero de elementos de la matriz
public CBanco(){
// Crear una matriz vaca
nElementos = 0;
clientes = new CCuenta[nElementos];
}
private void unElementoMs(CCuenta[] clientesActuales){
nElementos = clientesActuales.length;
// Crear una matriz con un elemento ms
clientes = new CCuenta[nElementos + 1];
// Copiar los clientes que hay actualmente
for ( int i = 0; i < nElementos; i++ )

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

La opcin elegida ser identificada por una sentencia switch y procesada de


acuerdo al esquema presentado a continuacin:
/////////////////////////////////////////////////////////////////
//////PROGRAMA 5.14
// Aplicacin para trabajar con la clase CBanco y la jerarqua
// de clases derivadas de CCuenta
//
import java.io.*;
public class Test7{
// Para la entrada de datos se utiliza Leer.class
public static CCuenta leerDatos(int op){
CCuenta obj = null;
String nombre, cuenta;
double saldo, tipoi, mant;
System.out.print("Nombre.................: ");
nombre = Leer.dato();
System.out.print("Cuenta.................: ");
cuenta = Leer.dato();
System.out.print("Saldo..................: ");
saldo = Leer.datoDouble();
System.out.print("Tipo de inters........: ");
tipoi = Leer.datoDouble();
if (op == 1){
System.out.print("Mantenimiento..........: ");
mant = Leer.datoDouble();
obj = new CCuentaAhorro(nombre, cuenta, saldo, tipoi, mant);
}
else{
int transex;
double imptrans;
System.out.print("Importe por transaccin: ");
imptrans = Leer.datoDouble();
System.out.print("Transacciones exentas..: ");
transex = Leer.datoInt();
if (op == 2)
obj = new CCuentaCorriente(nombre, cuenta, saldo, tipoi,
imptrans, transex);
else
obj = new CCuentaCorrienteConIn(nombre, cuenta, saldo,
tipoi, imptrans, transex);
}
return obj;
}
public static int men()
{
System.out.print("\n\n");
System.out.println("1. Saldo");

127

System.out.println("2. Buscar siguiente");


System.out.println("3. Ingreso");
System.out.println("4. Reintegro");
System.out.println("5. Aadir");
System.out.println("6. Eliminar");
System.out.println("7. Mantenimiento");
System.out.println("8. Salir");
System.out.println();
System.out.print(" Opcin: ");
int op;
do
op = Leer.datoInt();
while (op < 1 || op > 8);
return op;
}
public static void main(String[] args)
{
// Definir una referencia al flujo estndar de salida: flujoS
PrintStream flujoS = System.out;
// Crear un objeto banco vaco (con cero elementos)
CBanco banco = new CBanco();
int opcin = 0, pos = -1;
String cadenabuscar = null;
String nombre, cuenta;
double cantidad;
boolean eliminado = false;
do{
opcin = men();
switch (opcin){
case 1: // saldo
flujoS.print("Nombre o cuenta, total o parcial ");
cadenabuscar = Leer.dato();
pos = banco.buscar(cadenabuscar, 0);
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 2: // buscar siguiente
pos = banco.buscar(cadenabuscar, pos + 1);

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

El cuerpo de la interfaz puede incluir declaraciones de constantes y mtodos (no


sus definiciones).
La palabra extends significa que se est definiendo una interfaz que es una
extensin de otras; estas otras se especifican a continuacin de extends separadas
por comas. Una interfaz puede derivarse de ms de una superinterfaz. Una interfaz
as definida hereda todas las constantes y mtodos de sus superinterfaces, excepto
las constantes y mtodos que queden ocultos porque se redefinen.
UN EJEMPLO:
Las interfaces, al igual que las clases y mtodos abstractos, proporcionan las
plantillas de comportamiento que se espera sean implementados por otras clases.
Esto es, una interfaz Java declara un conjunto de mtodos, pero no los define (slo
aporta los prototipos de los mtodos). Tambin puede incluir definiciones de
constantes.
Como ejemplo, se realizar una interfaz IFecha que interactue entre dos clases
GregorianCalendar y CCuenta.

GregorianCalendar
Mtodo get

IFecha
Mtodo
da

Subclase de
CCUenta
Mtodo da

La clase GregorianCalendar es un proveedor de servicios; en nuestro ejemplo,


notificar el da a los objetos derivados de CCuenta cuando intenten ejecutar sus
mtodos comisiones o intereses. Para ello, como se muestra a continuacin, se
proporciona el mtodo get que devuelve el tipo de dato (da, mes, etc) solicitado:
public class GregorianCalendar extends Calendar {
//. . .
public final int get(tipo_de_dato){
//. . .
}
Cualquier objeto derivado de CCuenta que quiera utilizar un objeto
GregorianCalendar debe implementar el mtodo da proporcionado por la interfaz
IFecha. Este mtodo es el medio utilizado por el objeto GregorianCalendar para
notificar al objeto derivado de CCuenta el da actual. La interfaz IFecha puede
tener el siguiente aspecto:
///////////////////////////////////////////////////////////////
/ / / /PROGRAMA 5.15
//////////////////////////////////////////////////////////////////
// Interfaz IFecha: mtodos y constantes para obtener
//
el da, mes y ao
//
import java.util.*;
public interface IFecha{
public final static int DIA_DEL_MES = Calendar.DAY_OF_MONTH;
public final static int MES_DEL_AO = Calendar.MONTH;

131

public final static int AO = Calendar.YEAR;

public abstract int da();


public abstract int mes();
public abstract int ao();

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

public void comisiones(){


// Se aplican mensualmente por el mantenimiento de la cuenta
if (da() == 1) reintegro(cuotaMantenimiento);
}
public double intereses(){
if (da() != 1) return 0.0;
// 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;
}
// Implementacin de los mtodos de la interfaz IFecha
public int da(){
GregorianCalendar fechaActual = new GregorianCalendar();
return fechaActual.get(DIA_DEL_MES);
}
public int mes() { return 0; } // no se necesita
public int ao() { return 0; } // no se necesita
}
//////////////////////////////////////////////////////////////////
Como una interfaz slo aporta declaraciones de mtodos abstractos, es nuestra
obligacin definir todos los mtodos en cada una de las clases que utilice la
interfaz. No podemos elegir y definir slo aquellos mtodos que necesitemos. De
no hacerlo, Java obligara a que la clase fuera abstracta. Adems, el acceso a las
constantes es directo.
Si una clase implementa una interfaz, todas sus subclases heredarn los nuevos
mtodos que se hayan implementado en la superclase, as como las constantes
definidas por la interfaz. Modifiquemos la clase CCuentaCorriente para que utilice
tambin la interfaz IFecha:
//////////////////////////////////////////////////////////////////
/////PROGRAMA 5.17
//////////////////////////////////////////////////////////////////
// Clase CCuentaCorriente: clase derivada de CCuenta
//
import java.util.*;
public class CCuentaCorriente extends CCuenta implements IFecha{
// Atributos
private int transacciones;
private double importePorTrans;
private int transExentas;
// Mtodos
public CCuentaCorriente() {} // constructor sin parmetros

133

public CCuentaCorriente(String nom, String cue, double sal,


double tipo, double imptrans, int transex){
super(nom, cue, sal, tipo);
// invoca al constructor CCuenta
transacciones = 0;
// inicia transacciones
asignarImportePorTrans(imptrans); // inicia importePorTrans
asignarTransExentas(transex); // inicia transExentas
}
public void decrementarTransacciones(){
transacciones--;
}
public void asignarImportePorTrans(double imptrans){
if (imptrans < 0){
System.out.println("Error: cantidad negativa");
return;
}
importePorTrans = imptrans;
}
public double obtenerImportePorTrans(){
return importePorTrans;
}
public void asignarTransExentas(int transex){
if (transex < 0){
System.out.println("Error: cantidad negativa");
return;
}
transExentas = transex;
}
public int obtenerTransExentas(){
return transExentas;
}
public void ingreso(double cantidad){
super.ingreso(cantidad);
transacciones++;
}
public void reintegro(double cantidad){
super.reintegro(cantidad);
transacciones++;
}
public void comisiones(){
// Se aplican mensualmente por el mantenimiento de la cuenta
if (da() == 1){

134

int n = transacciones - transExentas;


if (n > 0) reintegro(n * importePorTrans);
transacciones = 0;
}
}
public double intereses(){
if (da() != 1) return 0.0;
// Acumular los intereses por mes slo los das 1 de cada mes
double interesesProducidos = 0.0;
// Hasta 3000 euros 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();
// Devolver el inters mensual por si fuera necesario
return interesesProducidos;
}
// Implementacin de los mtodos de la interfaz IFecha
public int da(){
GregorianCalendar fechaActual = new GregorianCalendar();
return fechaActual.get(DIA_DEL_MES);
}
public int mes() { return 0; } // no se necesita
public int ao() { return 0; } // no se necesita
}
//////////////////////////////////////////////////////////////////
Como la clase CCuentaCorriente implementa la intefaz IFecha, su subclase
CCuentaCorrienteConIn heredar los nuevos mtodos y constantes. Por lo tanto, no
es necesario agregar a la definicin de esta clase la palabra clave.
(Para poder ejecutar esta aplicacin en forma independiente, cree un proyecto con
el nombre que guste y guarde en l los archivos en el siguiente orden: Prog 5.1,
Prog 5.15, Prog 5.16, Prog 5.17, Prog 5.6, Prog 5.14., compile los programas en el
orden indicado y ejecute el Prog. 5.14)
El resultado obtenido desde el punto de vista grfico es el siguiente:

135

Clase Object

Class CBanco

Clase CCuenta

Class CuentaAhorro

Class CuentaCorriente

Interfaz IFecha

Class CuentaCorrienteConIn

En este caso, se obtiene el mismo resultado implementado la interfaz IFecha en la


superclase CCuenta, el haber implementado la interfaz en las subclases ha sido con
un fin didctico.
USO DE LA INTERFAZ COMO UN TIPO
Una interfaz es un tipo de datos; un tipo referenciado. Por lo tanto, el nombre de
una interfaz se puede utilizar en cualquier lugar donde pueda aparecer el nombre de
cualquier otro tipo de datos.
Por ejemplo, se puede declarar una matriz clientes que sea del tipo IFecha y
asignar a cada elemento un objeto de alguna de las subclases de CCuenta:
IFecha[ ] clientes = new IFecha[3];
CCuentaAhorro cliente0=newCCuentaAhorro();
clientes[0]=cliente0;
((CCuentaAhorro)clientes[0]).asignarNombre(cliente0);
System.out.println(cliente0.obtenerNombre());
// . . .
Una variable del tipo interfaz espera referenciar un objeto que tenga implementada
dicha interfaz, de lo contrario el compilador de Java mostrar un error.
Tambin es posible convertir implcitamente referencias a objetos que implementan
una interfaz en referencias a esa interfaz y viceversa, pero en este caso,
explcitamente. Lgicamente, con lo que sabemos podemos deducir que con una
referencia a la interfaz slo se tiene acceso a los mtodos y constantes declarados
en dicha interfaz.
El siguiente ejemplo muestra cmo tres clases no relacionadas, por el hecho de
implementar la misma interfaz Ixxx, permite definir una matriz de objetos de esa
clase y aplicar la definicin de poliformismo.
136

Interfaz Ixxx

Clase1

Clase2

Clase3

public interface Ixxx{


public abstract void m();
public abstract void p();
}
public class Clase1 implements Ixxx{
public void m(){
System.out.println("Mtodo m de Clase1");
}
public void p() {}
}
public class Clase2 implements Ixxx{
public void m(){
System.out.println("Mtodo m de Clase2");
}
public void p() {}
}
public class Clase3 implements Ixxx{
public void m(){
System.out.println("Mtodo m de Clase3");
}
public void p() {}
}
public class Test8{
public static void main(String[] args){
Ixxx objs[] = new Ixxx[3];
objs[0]=new Clase1();
objs[1]=new Clase2();
objs[2]=new Clase3();
for (int i=0;i<objs.length;i++)
objs[i].m();
}
}

137

INTERFAZ Y HERENCIA MULTIPLE.


A menudo se piensa en las interfaces como una alternativa a la herencia mltiple.
Pero la realidad es que ambos conceptos, interfaz y herencia mltiple, son bastante
diferentes, a pesar de que las interfaces pueden resolver problemas similares. En
particular:
Desde una interfaz, una clase slo hereda constantes
Desde una interfaz, una clase no puede heredar definiciones de mtodos
La jerarqua de interfaces es independiente de la jerarqua de clases. De
hecho, varias clases pueden implementar la misma interfaz y no pertenecer
a la misma jerarqua de clases. En cambio, cuando se habla de herencia
mltiple, todas las clases pertenecen a la misma jerarqua.
FUNCIN DE UNA INTERFAZ.
Una interfaz se utiliza para definir un protocolo de conducta que puede ser
implementado por cualquier clase en una jerarqua de clases. La utilidad que esto
pueda tener puede resumirse en los siguientes puntos:
Captar similitud entre clases no relacionadas sin forzar entre ellas una
relacin artificial. Una accin de este tipo permitir incluso, definir una
matriz de objetos de esas clases y aplicar, si fuera necesario, la definicin
de polimorfismo.
Declarar mtodos que una o ms clases deben implementar en determinadas
situaciones.
Suponga que se ha diseado una clase de objetos que pueden tener un
comportamiento especial siempre que implemente unos determinados
mtodos. Por ejemplo, cuando aprenda sobre applets y subprocesos
comprobar que usar un subproceso en un applet implica que la clase de
ste implemente la interfaz Runnable.
Publicar la interfaz de programacin de una clase sin descubrir cmo est
implementada.
En otro caso, otros desarrolladores recibiran la clase compilada y la
interfaz correspondiente.
IMPLEMENTAR MLTIPLES INTERFACES.
Una clase puede implementar una o ms interfaces. Por ejemplo:
public class miClase implements interfaz1, interfaz2, interfaz3{
//. . .
}
Cuando una clase implemente mltiples interfaces puede suceder que dos o ms
interfaces diferentes implementen el mismo mtodo. Si esto ocurre, proceda de
alguna de las formas indicadas a continuacin:
Si los mtodos tienen el mismo prototipo, basta con definir uno en la clase.
Si los mtodos difieren en el nmero o tipo de sus parmetros, estamos en
el caso de una sobrecarga del mtodo; implemente todas las sobrecargas.
Si los mtodos slo difieren en el tipo del valor de retorno, no existe
sobrecarga y el compilador produce un error, ya que dos mtodos
pertenecientes a la misma clase no pueden diferir slo en el tipo de
resultado.

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

public class Test9


{
public static void main(String[] args)
{
CPersona unaPersona = new CPersona("Su nombre", 22, 2, 2002);
System.out.println(unaPersona.obtenerNombre());
System.out.println(unaPersona.obtenerFechaNa());
}
}
En este caso, la fecha es un miembro de los datos relativos a la identificacin de
una persona, por lo que, una persona puede ser representada por una clase
CPersona y fecha por una clase CFecha.
En el ejemplo anterior, Java genera, adems del archivo Test9.class y
CPersona.class, el archivo CPersona$CFecha.class correspondiente a la clase
interna. Por lo que cada clase interna tiene su propio archivo de clase.
Tambin, Java permite definir una clase dentro de un mtodo. Ejemplo:
public class Clase2{
public void metClase2(int x, final int y){
int i=x+y;
final int c=x+y;
class Clase3{
int b=y;
void metClase3(){
System.out.println(b+c);
}
}
Clase3 obj=new Clase3();
obj.metClase3();
}
}
public class Clase1{
public static void main(String[] args){
Clase2 obj= new Clase2();
obj.metClase2(1,2);
}
}
Una clase definida dentro de un mtodo tiene unas reglas de acceso bastante
restrictivas. Un mtodo de una clase definida dentro de otro mtodo slo tiene
acceso a sus variables locales o parmetros formales declarados final. En este
ejemplo, metClase2 tiene parmetros formales x e y, y dos variables locales i y c.
El mtodo de la clase Clase3 definida dentro de metClase2 tiene acceso a las
variables locales y parmetros de este que han sido declarados final.

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

public void insertarTermino(CTermino obj){


// Insertar un nuevo trmino en orden ascendente del
// exponente de x; y a igual exponente de x, en orden
// ascendente del exponente de y.
if ( obj.obtenerCoeficiente() == 0 ) return;
int k = 10, i;
int expX = obj.obtenerExponenteDeX();
int expY = obj.obtenerExponenteDeY();
// Si el trmino en xy existe, sumar los coeficientes
for ( i = nElementos - 1; i >= 0; i-- )
{
if ( expX == trminos[i].obtenerExponenteDeX() &&
expY == trminos[i].obtenerExponenteDeY() ){
double coef = trminos[i].obtenerCoeficiente() +
obj.obtenerCoeficiente();
if ( coef != 0 )
trminos[i].asignarCoeficiente(coef);
else
eliminarTermino(i);
return;
}
}
// Si el trmino en xy no existe, insertarlo.
while (Math.abs(expX) > k || Math.abs(expY) > k) k = k*10;
// Se aade un elemento vaco
unElementoMs(trminos);
i = nElementos - 2; // i = nElementos - 1 vale null
while ( i >= 0 && (expX * k + expY <
trminos[i].obtenerExponenteDeX() * k +
trminos[i].obtenerExponenteDeY()) ){
trminos[i+1] = trminos[i];
i--;
}
trminos[i+1] = obj;
}
public boolean eliminarTermino(int i){
// Eliminar el objeto que est en la posicin i
if (i >= 0 && i < nElementos){
trminos[i] = null; // enviar el objeto a la basura
unElementoMenos(trminos);
return true;
}
return false;
}
public CTermino trminoEn(int i){
// Devolver la referencia al objeto i de la matriz

145

if (i >= 0 && i < nElementos)


return trminos[i];
else{
System.out.println("ndice fuera de lmites");
return null;
}
}
public int longitud() { return nElementos; }
public CPolinomio copiar(CPolinomio p) // asignacin
{
// Copiar el origen en el nuevo destino
nElementos = p.nElementos;
trminos = new CTermino[nElementos];
for (int i = 0; i < nElementos; i++)
trminos[i] = new CTermino(p.trminos[i]);
return this;
}
public CPolinomio sumar(CPolinomio pB){
// pR = pA.sumar(pB). pA es this y pR el resultado.
int ipa = 0, ipb = 0, k = 0;
int na = nElementos, nb = pB.nElementos;
double coefA, coefB;
int expXA, expYA, expXB, expYB;
CPolinomio pR = new CPolinomio(); // polinomio resultante
// Sumar pA con pB
while ( ipa < na && ipb < nb ){
coefA = trminos[ipa].obtenerCoeficiente();
expXA = trminos[ipa].obtenerExponenteDeX();
expYA = trminos[ipa].obtenerExponenteDeY();
coefB = pB.trminos[ipb].obtenerCoeficiente();
expXB = pB.trminos[ipb].obtenerExponenteDeX();
expYB = pB.trminos[ipb].obtenerExponenteDeY();
k = 10;
while (Math.abs(expXA) > k || Math.abs(expYA) > k) k = k*10;
if ( expXA == expXB && expYA == expYB ) {
pR.insertarTermino(new CTermino(coefA + coefB, expXA, expYA));
ipa++; ipb++;
}
else if (expXA * k + expYA < expXB * k + expYB){
pR.insertarTermino(new CTermino(coefA, expXA, expYA));
ipa++;
}
else{
pR.insertarTermino(new CTermino(coefB, expXB, expYB));
ipb++;
}

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

// Suma de polinomios dependientes de dos variables.


// Esta aplicacin utiliza la clase Leer.
//
public class Test{
public static CTermino leerTermino(){
CTermino ptx = null;
double coef;
int expx, expy;
System.out.print("Coeficiente: ");
coef = Leer.datoDouble();
System.out.print("Exponente en X: ");
expx = Leer.datoInt();
System.out.print("Exponente en Y: ");
expy = Leer.datoInt();
System.out.println();
if ( coef == 0 && expx == 0 && expy == 0 ) return null;
ptx = new CTermino( coef, expx, expy );
return ptx;
}
public static void main(String[] args)
{
// Definir los polinomios a sumar
CPolinomio polinomioA = new CPolinomio();
CPolinomio polinomioB = new CPolinomio();
// Declarar una referencia al polinomio resultante
CPolinomio polinomioR;
// Declarar una referencia a un trmino cualquiera
CTermino ptx = null; // puntero a un trmino
// Leer los trminos del primer sumando
System.out.print("Trminos del polinomio A "
+ "(para finalizar introduzca 0 para el\n"
+ "coeficiente y para los exponentes).\n\n");
ptx = leerTermino();
while ( ptx != null ){
polinomioA.insertarTermino( ptx );
ptx = leerTermino();
}
// Leer los trminos del segundo sumando
System.out.println("Trminos del polinomio B "
+ "(para finalizar introduzca 0 para el\n"
+ "coeficiente y para los exponentes).\n\n");
ptx = leerTermino();
while ( ptx != null )
{
polinomioB.insertarTermino( ptx );
ptx = leerTermino();
}
// Sumar los dos polinomios ledos
polinomioR = polinomioA.sumar(polinomioB);

148

// Visualizar el primer sumando


System.out.print("Polinomio A: ");
polinomioA.mostrarPolinomio();
System.out.println();
// Visualizar el segundo sumando
System.out.print("Polinomio B: ");
polinomioB.mostrarPolinomio();
System.out.println();
// Visualizar el polinomio suma
System.out.print("Polinomio R: ");
polinomioR.mostrarPolinomio();
System.out.println();
// Visualizar el valor del polinomio suma para x = 1 e y = 1
System.out.println("Para x = 1 e y = 1, el valor es: " +
polinomioR.valorPolonomio(1, 1));
}
}

149

You might also like