Vous êtes sur la page 1sur 13

Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 1/13

COMPILACION INTELIGENTE EN JAVA.


Michel Estrada y Miguel Katrib
Dpto. de Ciencia de la Computacin, Universidad de la Habana.
email: mkm@matcom.uh.cu.

Publicado en Soluciones Avanzadas, Mxico, Abril 1999
Los entornos integrados de programacin actuales para lenguajes como Pascal o C++, utilizan recursos
extralingsticos como los proyectos y make files para organizar las distintas unidades de programa que
conforman una aplicacin. Sin embargo, estos recursos son insuficientes para detectar cules modificaciones a
una unidad de programa pueden provocar a su vez cambios en otras unidades o al menos requerir la
recompilacin de stas. En el presente trabajo se analiza y expone una estrategia de optimizacin para evitar
recompilaciones innecesarias de las clases que componen un programa en Java la cual se sustenta en la
equivalencia entre el concepto de clase y de unidad de compilacin que se tiene en un LOO no hbrido como
Java.
Introduccin
Una de las intenciones de dividir una aplicacin en diferentes unidades o mdulos es facilitar la
localizacin de las modificaciones y optimizar el tiempo de compilacin de la aplicacin, al tratar de evitar la
recompilacin de una unidad si sta alguna vez fue compilada en una versin anterior de la misma aplicacin
o de otra aplicacin que tambin la utilizase.
Algunas variantes de Pascal, como Delphi, introducen recursos lingsticos para expresar estas unidades
(units). Otros lenguajes como C++ expresan esto de forma extralingstica con la divisin de archivos de
encabezado (archivos con extensin .h), archivos fuentes (archivos con extensin .cpp) y archivos de cdigo
(archivos con extensin .obj en el caso de los sistemas DOS y Windows). La generalidad de los compiladores
tradicionales (este es el caso de la mayora de los compiladores de C++ y Pascal) deciden sobre la necesidad
de compilar o no una determinada unidad a partir de las marcas de tiempo (timestamp)
1
de los archivos de
texto fuente (.pas en Pascal, .h y .cpp en C++) y de los archivos correspondientes a las unidades compiladas
(archivos con extensin .obj). Es decir, la compilacin de una aplicacin en la que participan las unidades A y
B (ya compiladas alguna vez) y en la que B depende de alguna forma de A, requerir la recompilacin de la
unidad B si la unidad A tiene una marca de tiempo posterior a la de B (lo cual debe significar que se le hizo
una modificacin a A despus de la ltima compilacin de B). Una de las limitaciones de este enfoque es que
no se tiene en cuenta si la naturaleza de la modificacin llevada a cabo sobre la unidad A requiere realmente la
recompilacin de B.
A continuacin se presenta una estrategia de compilacin inteligente para el lenguaje Java que intenta
evitar recompilaciones innecesarias.
Estrategia de Compilacin Inteligente.
Para entender la estrategia de compilacin inteligente primero es necesario conocer los distintos estados
(archivos) que se asocian a una clase Java. Tenemos el texto fuente de una o varias clases en Java (archivos
con extensin .java). La compilacin de estos archivos .java nos da archivos en un cdigo virtual
denominado bytecode (archivos con extensin .class) que corresponde a uno por cada clase compilada. El
intrprete de este bytecode es denominado Mquina Virtual de Java
2
. En Java existen dos tipos bsicos de
programas : los applets y las aplicaciones independientes. Los applets son referidos en una pgina HTML (o
pgina Web) y ejecutados cuando la pgina es cargada por un navegador
3
(como Internet Explorer o Netscape
Navigator) que contenga una JVM. Las aplicaciones independientes no requieren de un navegador para
correr, pueden ser ejecutados desde la lnea de comando usando un intrprete independiente.
Una clase B, que depende de una clase A, se compilar porque se indica explcitamente (por ejemplo
debido a alguna operacin escogida en el entorno de programacin), o por que se manda a generar una
aplicacin que involucra a la clase B, y no existe su correspondiente archivo .class, o ste ha quedado
inhabilitado (por ejemplo el archivo .class de la clase A tiene una marca de tiempo posterior al de la clase B).

1
Por lo general fecha y hora en que fue actualizado o creado el archivo.
2
JVM del ingles Java Virtual Machine.
3
Tambin conocido como browser.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 2/13
Debe notarse que el cambio introducido en el archivo texto de A y que dio lugar a la recompilacin y la
existencia por tanto de un nuevo archivo .class para A con fecha posterior a la del .class de B puede ser tan
inocuo como la introduccin de un cambio de lnea o un espacio para mejorar la legibilidad. Aunque un
cambio de este tipo podr implicar innecesariamente la recompilacin de todas las clases que estn en el
mismo archivo .java (paquete), la intencin de la compilacin inteligente es no propagar la recompilacin
(como s ocurre en muchos entornos de C++ basados slo en las marcas de tiempo) a otras clases (como B) en
otros archivos .java. El inters del presente trabajo es mostrar que existen cambios, que aunque no tan
triviales como el anterior, tampoco provocan este efecto de propagacin.
Modificaciones a una clase y afectaciones que pueden provocar a otras.
En la Tabla 1 cada fila indica posibles modificaciones en el texto de una clase A, las dos primeras
columnas indican las repercusiones que estas modificaciones pueden provocar en el texto fuente de sus
herederos (clases que hagan extends de A) o de sus clientes (clases que usen A) implicando entonces la
recompilacin de estas clases. De este modo, la celda j,k representa la repercusin que la modificacin j puede
tener en el archivo que indica la columna k.
La mayora de estas modificaciones lo que pueden implicar provocar en el texto fuente de clases
herederas o clientes son afectaciones pot enciales. Esto quiere decir que para determinar si realmente hay
afectaciones habra que analizar el texto fuente de la clase heredera y (o) de la clase cliente. Por supuesto que
no es prctico pretender hacer esto cuando se detecta la modificacin en A (porque incluso la aplicacin que
ha involucrado a A con su modificacin no tiene por qu involucrar a un cliente o a un heredero B de A).
Tampoco tiene sentido prctico pretender determinar si hay que recompilar B analizando su texto fuente pues
ste anlis is puede ser tan costoso como la propia recompilacin.
Lo que se propone con la presente estrategia es que cuando se detecte una de estas modificaciones en A se
marque a estos clientes y (o) herederos de modo que en un uso posterior de los mismos (puede que sea o no
en la misma aplicacin que ha modificado a A) esta marca sirva para indicar que hay que recompilarlos.
Note que posiblemente el propio programador que hizo el cambio en el fuente de A, tambin haga los cambios
necesarios en los fuentes de las clases que dependan de A. En este caso cambiara el timestamp del archivo
texto del cliente o heredero B y por tanto habra que recompilarlo de todos modos.
Los beneficios de esta estrategia son entonces:
1. Deteccin temprana de problemas: si el programador no realiz cambios en el texto del cliente o heredero
B entonces la marca efectuada en B al compilar A servir para forzar la compilacin de B y detectar los
cambios que hay que hacer.
2. Eficiencia en el proceso de compilacin: La ausencia de marca en B servir entonces para decidir no
recompilar B, aunque dependa de A y est involucrada en la compilacin de una aplicacin posterior a la
marca de tiempo de A.

Notacin utilizada.
Para hacer menos extenso el presente trabajo, los ejemplos utilizarn tres puntos suspensivos (...) para
indicar un segmento de texto Java (apropiado al contexto en que se use) pero que no es relevante para ilustrar.
Un asterisco(*) en una celda de la tabla significa que el cambio producido afecta a todos los clientes y (o)
herederos (si la clase es pblica se afectaran los clientes y (o) herederos que estn fuera y dentro del paquete,
pero sino lo es, sern afectados los clientes y (o) herederos de la clase que estn en su mismo paquete, todo
depender del acceso que tenga la clase).
Dos asteriscos(**) en las columnas de la tabla significa que el cambio efectuado afecta solamente a los
clientes o (y) herederos que estn en otro paquete diferente al de la clase (para que esto ocurra la clase tiene
que ser pblica). Por consiguiente una indicacin con * implica una **.
Un (.class) en las columnas de la tabla significa que el cambio efectuado en una clase no afecta a los
fuentes de las clases clientes y (o) herederas pero de todos modos es necesario una recompilacin de estas
clases para que se actualice la informacin que est incrustada en sus correspondientes archivos .class.
Casos
1 El texto fuente de un heredero directo puede quedar errneo.
Una indicacin (*, **, .class) en alguna celda de la primera columna significa que despus de efectuar en
una clase A la modificacin indicada en la fila, una clase A1, heredera de A, puede tener errores y por tanto el
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 3/13
.class de la clase A1 quedar marcado. Esto significa que la prxima vez que la clase A1 vaya a ser
utilizada, hay que comp ilar su archivo texto para generar un nuevo .class.
2 El texto fuente de un cliente queda malo.
Una indicacin (*, **, .class) en una celda de la columna 2 significa que despus de efectuar en una clase
A la modificacin indicada en la fila, una clase B cliente directo de A puede tener errores y por tanto el .class
de B quedar marcado. Esto significa que dicha clase B deber recompilarse cuando se necesite en una
aplicacin.
3 Control en otros entornos de Java.
En la columna 3 se muestra la forma en que el entorno de JBuilder
4
controla las modificaciones
indicadas en cada fila. A la vez esta columna esta dividida en dos subcolumnas:
1. En la primera se indica si este entorno detecta la modificacin (se pone un si.) o no (se pone no.) en
tiempo de compilacin, esto quiere decir que recompilar los archivos fuentes de las clases herederas
y(o) clientes que sern afectadas por la modificacin hecha, para que los programadores puedan arreglar
los errores que se introdujeron (errores que podran salir en ejecucin) con dicha modificacin en los
herederos y(o) clientes. Por ejemplo, cuando se elimina un miembro de una clase (campos y mtodos
declarados en la clase, heredados de su superclase o heredados de las interfaces que implementa)
5
, se
compilarn los clientes y herederos (de esta clase) que estn usando el miembro que se elimin, entonces
se generar un error en compilacin indicando que el miembro eliminado no se encuentra en la clase. En
los casos en que la modificacin sea detectada en tiempo de compilacin, no se indicar nada en la otra
subcolumna, porque esta modificacin no producir errores en ejecucin.
2. En la segunda subcolumna se indica si este entorno detecta o no las modificaciones (que no se detectaron
en compilacin) en tiempo de ejecucin. Es decir, si de alguna forma dan a conocer los errores que se
introdujeron con estas modificaciones; ejemplo, elevando excepciones que brindan informacin sobre los
errores.
Cuando se compila en JBuilder, ste analiza la naturaleza de los cambios que se hizo al fuente de la clase
y decide si recompila o no los archivos fuentes de los herederos y (o) clientes de dicha clase, para lograr esto,
JBuilder crea un archivo con extensin dependency (la primera vez que se compila un archivo .java) en el
subdirectorio de salida junto con los archivos .class. Este archivo contiene informacin detallada sobre qu
clases usan a cules (de las clases que se es cliente y de las que se heredan), para cada clase en el paquete.
Analizando estas dependencias de las clases que guarda en dicho archivo, JBuilder recompilar o no los
archivos fuentes de las clases clientes y (o) herederas afectadas.
En cuanto a Visual J++
6
, este por defecto recompila todas las clases involucradas en el proyecto cada vez
que un archivo fuente de una clase se modifica. Esto claro est, no es muy eficiente (se pierde tiempo
compilando las clases que no fueron modificadas, mucho mas si el proyecto es grande) pero evita que en
ejecucin se produzcan errores, detectndolos en compilacin. En el dilogo de las propiedades del proyecto,
Visual J++, ofrece una opcin para compilar, solamente, los archivos que son modificados. A diferencia del
otro mtodo utilizado (el de compilar todas las clases), este es ms eficiente al compilar menos archivos pero
menos efectivo en el control de los errores que puedan producir los cambios efectuados, estos sern conocidos
en ejecucin a travs de las excepciones que dispara la JVM.
JBuilder

Aspectos a Comparar
H
e
r
e
d
e
r
o

f
u
e
n
t
e
.

C
l
i
e
n
t
e

f
u
e
n
t
e
.

Detecta
en
Compil.
Detecta
en
Ejecucin
1- Poner especificacin abstract a clase. * no. si.
2- Poner especificacin final a clase. * no. no.
3- Quitar especificacin public a clase. ** ** si.
4- Introducir un ancestro o interface.
* no. no.
5- Eliminar un ancestro o interface.
* * no. no.
6- Agregar constructor.
* * si.

4
Ambiente visual para Java de Enprise Corp.
5
Los constructores de una clase no son miembros.
6
Ambiente visual para Java de Microsoft Corp.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 4/13
7- Eliminar constructor.
* * si.
8- Cambiar constructor pblico a constructor sin
especificacin de visibilidad.
** ** si.
9- Cambiar constructor pblico a protegido.
** si.
10-Cambiar construc tor protegido a constructor sin
especificacin de visibilidad.
** si.
11- Hacer miembro o constructor privado.
* * si.
12- Cambiar miembro pblico a protegido.
** si.
13- Cambiar miembro pblico a miembro por defecto.
** ** si.
14- Cambiar miembro protegido a miembro sin
especificacin de visibilidad.
** si.
15- Cambiar mtodo protegido a pblico.
* si.
16- Cambiar mtodo por defecto a pblico o
protegido.
* si.
17- Cambiar mtodo privado. * si.
18- Eliminar miembro.
* * si.
19- Agregar campo.
* * no. no.
20- Agregar mtodo.
*
.class
si.
21- Cambiar tipo a campo.
* * si.
22- Cambiar tipo retornado por mtodo.
* * si.
23- Cambiar tipo a parmetro.
* * si.
24- Agregar o Eliminar un parmetro.
* * si.
25- Cambiar inicializacin a campo final. .class .class
si.
26- Agregar clusula throws. * * si.
27- Cambiar la clusula throws. * * si.
28- Poner especificacin final a campo. * * si.
29- Eliminar especificacin final a campo. .class .class
si.
30- Quitar especif icacin static a campo.
.class
* si.
31- Definir un campo como esttico. .class .class
si.
32- Adicionar o quitar especificacin volatile.
.class .class
si.
33- Agregar especificacin abstract a mtodo. * si.
34- Poner especificacin final a mtodo. * si.
35- Hacer esttico un mtodo. *
.class
si.
36- Eliminar especificacin static a mtodo. * * si.
Tabla 1. Modificaciones a una clase que pueden provocar afectaciones a otras clases.
En las tablas con los ejemplos a continuacin, la primera fila ilustra la situacin antes del cambio, la fila
intermedia indica la modificacin que se har y la ltima fila indica la situacin despus del cambio.
1. Poner especificacin abstract a clase (hacer abstracta una clase).
1.2 El texto de un cliente B puede quedar incorrecto si intenta crear instancias de una clase que es
abstracta.
class A{
...
}
class B{
A aa = new A();
...
}
haciendo la clase A abstracta.
abstract class A{
...
}
en la clase B no se puede crear
objetos de la clase A por ser abstracta.
Nota: En Java una clase puede ser declarada abstracta y no tener mtodos abstractos, por esto los
herederos no se afectan al ponerle abstract a una clase (la modificacin de hacer un mtodo abstracto,
ser analizada ms adelante).
2. Poner especificacin final a clase, o sea cambiar una clase sin especificacin o abstracta a clase final.
2.1 Si una clase se hace final entonces de ella no se puede heredar.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 5/13
class A{
public void f(){...}
...
}
class A1 extends A{
...
}
haciendo la clase A f inal.
final class A{
public void f(){...}
...
}
la clase A1 no puede heredar de la
clase A ya que esta clase se hizo final.
2.2 Note que por este cambio no tiene que quedar afectado un cliente. Lo que antes el cliente poda estar
haciendo con la clase A ahora lo puede seguir haciendo.
3. Quitar especificacin public a clase en un paquete, esto implica que la clase no puede ser accedida fuera
del paquete en que se encuentra.
3.1, 3.2 Esta modificacin afecta lo mismo a clientes que a herederos, sino es tn en el mismo paquete de
la clase que se modific.
package java.proveedor;
public class A{
...
}
package cliente;
import java.proveedor;
class A1 extends A{
...
}
cambiando el acceso a la clase A.
package java.proveedor;
class A{
...
}
la clase A no se exporta fuera del
paquete al tener acceso por defecto, la
clase A1 no puede heredar de una clase
a la que no tiene acceso.
4. Introducirle un ancestro o interfaces (interface) a una clase.
4.1 Esta modificacin puede afectar a los herederos de la clase, s con el nuevo ancestro se introduce un
mtodo que en los herederos ya existe pero con el tipo de retorno diferente.
class A{
...
}

class A1 extends A{
char f(){...}
...
}
introducir S como ancestro de A.
class S{
void f(){...}
...
}
class A extends S{
...
}
la clase A1 queda errnea al tener dos
funciones f() que retornen tipos
diferentes.
5. Eliminar ancestro o interface (interface) a la clase.
5.1, 5.2 Es trivial por que los herederos y los clientes se afectan si estuviesen haciendo uso de un
miembro perteneciente a la clase o a la interface que ahora se ha eliminado.
6. Agregar constructor con parmetros a una clase que no tenia constructor.
6.1 Los herederos directos estn obligados a llamar explcitamente al constructor del padre para crear el
objeto padre.
class A{
...
}
class A1 extends A{
A1(){...}
...
}
agregando constructor a A.
class A{
A(int i){...}
...
}
al agregarse un constructor a la clase A,
la clase A1 quedara errnea porque no
lo esta llamando.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 6/13
6.2 Los clientes al crear instancias de la clase, tendrn que usar el nuevo constructor que recibe
parmetros.
class A{
...
}
class B{
A aa = new A();
...
}
agregando constructor a A.
class A{
A(int i){...}
...
}
la clase B no puede crear objetos de A,
a travs de un constructor que no existe
(ha sido modificado).
7. Eliminar constructor que tiene parmetros.
7.1 Los herederos directos sern afectados al estar llamando a un constructor en el padre que no existe.
class A{
A(int i){...}
...
}
class A1 extends A{
A1(){
A(3);
...
}
...
}
eliminando el constructor a A.
class A{
...
}
la clase A1 queda errnea al intentar
llamar a un constructor de la clase A que
se elimin.
7.2 Los clientes que estaban creando instancias de la clase a travs del constructor que se elimino, quedan
errneos al no existir dicho constructor.
8. Cambiar constructor pblico (public) a constructor sin especificacin de visibilidad.
8.1 Los herederos directos que se encuentran fuera del paquete no pueden acceder al constructor de la
superclase, este siempre se llama para inicializar los valores de los campos.
package java.proveedor;
public class A{
public A(){...}
...
}

package cliente;
import java.proveedor;
class A1 extends A{
A1(){...}
...
}
cambiando acceso a constructor.
package java.proveedor;
public class A{
A(){...}
...
}
la clase A1 queda errnea, ahora que al
constructor de A no se puede acceder
desde otro paquete.
8.2 Los clientes que no estn en el mismo paquete no pueden crear instancias de la clase, ya que no tienen
acceso al constructor.
package java.proveedor;
public class A{
public A(){...}
...
}

package cliente;
import java.proveedor;
class B{
A aa = new A();
...
}
cambiando acceso a constructor.
package java.proveedor;
public class A{
protected A(){...}
...
}
la clase B no tiene acceso al constructor
de A para crear instancias.
9. Cambiar constructor pblico (public) a protegido (protected).
9.2 A los clientes les pasara los mismo que en 8.2.
10. Cambiar constructor protegido (protected) a constructor sin especificacin de visibilidad.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 7/13
10.1 Por las mismas razones que en 8.1, los herederos directos de la clase quedaran errneos.
11. Hacer un miembro o constructor privado (private).
11.1, 11.2 Los clientes y los herederos, tanto en el mismo paquete como fuera, que estn haciendo uso de
este recurso se vern afectados por no tener acceso al miembro o constructor que ahora es
privado y no ser alcanzado por nadie.
class A{
public int aa;
...
}

class A1 extends A{
void g(){
aa=2;
...
}
...
}
Haciendo aa privado.
class A{
private int aa;
...
}
como no se tiene alcance al campo aa
privado de la clase A, la clase A1 ser
afectada.
12. Cambiar miembro pblico (public) a protegido (protected).
12.2 Los clientes fuera del paquete que estn usando este miembro no tendrn acceso a ste.
package java.proveedor;
public class A{
public int aa;
...
}

package cliente;
import java.proveedor;
class B{
void g(){
int b = new A().aa;
...
}
...
}
haciendo el campo aa protegido.
package java.proveedor;
public class A{
protected int aa;
...
}
la clase B no podr usar el campo de la
clase A por encontrarse en otro paquete
y ser protegido.
13. Cambiar miembro pblico (public) a miembro sin especificacin de vis ibilidad.
13.1, 13.2 Los clientes y los herederos que estn usando este miembro (y se encuentren en otro paquete)
sern afectados al restringrsele el acceso a este miembro.
package java.proveedor;
public class A{
public int aa;
...
}

package cliente;
import java.proveedor;
class A1 extends A{
void g(){
aa=2;
...
}
...
}
poner aa con acceso por defecto.

package java.proveedor;
public class A{
int aa;
...
}
la clase A1 no podr usar el campo aa
de la clase A por encontrarse en otro
paquete y tener acceso para el paquete.
14. Cambiar miembro protegido (protected) a miembro por defecto.
14.1 Los herederos fuera del paquete sern afectados por lo mismo que en 13.1.
15. Cambiar mtodo protegido (protected) a pblico (public).
15.1 Si los herederos estn redefiniendo como protected dicho mtodo, quedarn errneos al tener este
menos acceso que en la superclase. En Java (a diferencia de C++) no se permite restringir el tipo de
acceso.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 8/13
class A{
protected void f(){...}
...
}

class A1 extends A{
protected void f(){...}
...
}
haciendo f() pblico.
class A{
public void f(){...}
...
}
el mtodo f() de la clase A1 no puede
reducir el acceso del mtodo f() en la
clase A.
16. Cambiar un mtodo por defecto a pblico (public) o protegido (protected).
16.1 Los herederos pueden quedar afectados si el mtodo que se cambia lo redefinen, porque los mtodos
que se redefinen no pueden tener ms visibilidad en la superclase que en los herederos.
17. Cambiar mtodo privado a mtodo por defecto, pblico o protegido.
17.1 Si el mtodo que se cambia tiene igual signatura que alguno de los que se encuentra en el heredero
pero el tipo de retorno es diferente, entonces se afecta al heredero.
class A{
privado void f(){...}
...
}

class A1 extends A{
public int f(){...}
...
}
haciendo pblico f().
class A{
public void f(){...}
...
}
la clase A1 no puede contener dos
mtodos con el mismo nombre y que
devuelvan tipos diferentes.
Nota: Recuerde que en Java no se puede sobrecargar un mtodo si slo se diferencia en el tipo de
retorno. Mientras f() era privada en A no se consideraba como mtodo del heredero A1.
18. Eliminar un miembro.
18.1, 18.2 Es trivial que los herederos y los clientes son afectados pues pueden estar haciendo uso del
miembro que se elimin. Hay dos modificaciones que no se incluyeron en la tabla porque se
consideraron como formas particulares de interpretar esta modificacin :
- Cambiar un mtodo a campo.
- Pasar un campo a mtodo.
- Cambiar el nombre de un miembro.
19. Introducir un campo que est declarado en un ancestro.
19.1, 19.2 El campo introducido ocultara al del ancestro y habra que cambiar la referencia a este, por
esto los herederos y clientes quedan afectados. S el campo introducido es de diferente tipo al
que se encuentra en el ancestro, los fuentes de los clientes y herederos serian afectados, sino
los .class sern los afectados.
class S{
public String b;
...
}
class A extends S{
...
}

class B{
void f(A a){
String b1 = a.b;
...
}
...
}
agregando campo b a A.
class A extends S{
public char b;
...
}
no se puede asignar al campo b1 un
valor del campo b, ya que difieren en el
tipo.
20. Agregar un mtodo.
20.1 Los herederos sern afectados por las mismas razones que 4.1.
20.2 Los .class de los clientes pueden quedar errneos si el mtodo agregado esta sobrecargando otro
mtodo que antes tenia parmetros ms generales (con tipos superiores en la jerarqua) que los
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 9/13
nuevos parmetros. El fuente del cliente no sufre modificaciones pero hay que recompilarlo para que
el .class trabaje con el mtodo adecuado.
Nota: En este ejemplo se utilizaran los identificadores C y C1 para denotar dos clases cualquiera en
Java donde la clase C1 hereda de la clase C.
class A{
void f(C c){...}
...
}

class B{
void g(A a1){
a1.f(new C1());
...
}
...
}
sobrecargando a f().
class A{
void f(C c){...}
void f(C1 c1){...}
...
}
la clase B seguir llamando a f(C)
sino se recompila, es necesario su
recompilacin para que haga el
llamado al nuevo mtodo f(C1).
21. Cambiar el tipo a un campo.
21.1, 21.2 Con este cambio los herederos y los clientes pueden estar usando el campo como si fuera de un
tipo que no es porque se cambio.
class A{
public int aa;
...
}
class A1 extends A{
void f(){
int a1 = aa;
...
}
...
}
cambiando tipo al campo aa.

class A{
public double aa;
...
}
no se puede hacer a1 = aa al ser de
tipos diferentes.
22. Cambiar el tipo de retorno en un mtodo.
22.1,22.2 Los herederos y los clientes tendrn que modificarse si quedan usando incorrectamente el valor
que ahora retorna el mtodo.
class A{
public boolean f(){...}
...
}

class B {
void g(A a1) {
if (a1.f()){...}
...
}
...
}
cambiando tipo retorno por f().
class A{
public String f(){...}
...
}
la clase B queda errnea al estar
usando el mtodo f() de la clase A
como si retornara boolean y este
retorna ahora un String.
23. Cambiando el tipo a un parmetro.
23.1, 23.2 Los herederos y los clientes que estn llamando a este mtodo, debern modificar el l lamado al
mtodo por diferir en el tipo de los parmetros.
Nota: S el tipo introducido es ms general que el que estaba, entonces los fuentes de los
clientes y herederos no sern afectados pero habr que recompilarlos para que se actualice la
referencia al mtodo; s no se compilan, se producir un error de linkeo en ejecucin.
class A{
public void f(int a) {
...
}
...
}
class A1 extends A{
void g() {
f(2);
...
}
...
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 10/13
}
cambiando tipo a parmetro.

class A{
public void f(char a){
...
}
...
}
el llamado al mtodo f() en la clase
A1 esta incorrecto por que el parmetro
que recibe es de tipo char.
24. Agregar o Eliminar un parmetro.
24.1, 24.2 Al cambiar la cantidad de parmetros los herederos y clientes que llamen al mtodo quedarn
afectados al no coincidir la cantidad de parmetros reales con la de los parmetros formales.
class A{
public void f(int a, char c){
...
}
...
}

class B{
void g(A aa){
aa.f(2, A);
...
}
...
}
eliminado un parmetro.
class A{
public void f(int a){...}
...
}
no coinciden la cantidad de parmetros
reales en la clase A y los formales en B del
mtodo f().
25. Cambiando la inicializacin a campo final. Este valor esta inmerso dentro del archivo .class respectivo
por lo que hay que volver a generarlos.
25.1, 25.2 Aunque el texto fuente de los herederos y los clientes (que usen este campo) no son afectados,
estos debern ser compilados para la actualizacin adecuada del valor inicial del campo en el
archivo .class.
class A{
public static final int aa = 2;
...
}

class A1 extends A{
public int b = aa;
...
}
Cambiando inicializacin a aa.
class A{
public static final int aa = 4;
...
}
al cambiar el valor de aa, es necesario
compilar la clase A1 para que el nuevo
valor del campo aa quede
correctamente asignado al de b.
26. Agregar clusula throws a mtodo o constructor.
26.1, 26.2 Java exige que los clientes y herederos de una clase que hacen uso de los mtodos y(o)
constructores que tengan clusula throws usen una clusula try...catch para la captura de
las excepciones que puedan producirse o que introduzcan una clusula throws.
class A{
public void f(){...}
...
}

class B{
void g(A aa){
aa.f();
...
}
...
}
agregando throws a mtodo.
class A{
public void f() throws Exception{
...
}
...
}
el mtodo g() de B tendr que introducir un
bloque try...catch para la captura de la
excepcin que puede disparar f() o
tendran que introducir una clusula throws
Exception.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 11/13
27. Cambiar la clusula throws de un mtodo o de un constructor.
27.1 Al cambirsele la clusula a un mtodo, los herederos que redefinan este mtodo pueden quedar
afectados si la excepcin que se introdujo es ms especifica que la que se encuentra en el mtodo
redefinido.
class A{
void g() throws Exception{
...
}
...
}

class A1 extends A{
void g() throws Exception{
...
}
...
}
cambiando tipo de excepcin.
class A{
void g() throws SecurityException{
...
}
...
}
en la clusula throws del mtodo g() las
excepciones deben ser ms especificas
que las del mtodo g() en la superclase.
27.2 Los clientes pueden quedar afectados por lo mismo que en el ejemplo 26.2.
28. Poner la especificacin final a un campo.
28.1, 28.2 Ni los herederos, ni los clientes pueden asignarle valor a un campo que sea final, ya que a
este tipo de campo solo se le asigna valor en la inicializacin de la clase.
class A{
public int aa;
...
}

class A1 extends A{
void g(){
aa = 3;
...
}
...
}
haciendo final campo aa.
class A{
public final int aa = 1;
...
}
la clase A1 queda incorrecta, no se puede
asignar un valor al campo aa por ser final.
29. Eliminar especificacin final a campo.
29.1, 29.2 La informacin que esta en los .class de los clientes y herederos (sobre este campo) debe ser
actualizada para que se puedan reflejar los prximos cambios que se le hagan al campo.
30. Quitar la especificacin esttica (static) a un campo.
30.1, 30.2 Los clientes no podrn hacer referencias a este campo a travs del nombre de la clase. Al
hacer este cambio, el campo tendr que ser accedido a travs de instancias de la clase. Los
.class de los herederos sern afectados porque la informacin que ellos tienen es que el
campo es esttico y se tiene que actualizar.
class A{
public static boolean aa = true;
...
}

class B{
void g(){
if(A.aa){...}
...
}
...
}
el campo aa deja de ser esttico.
class A{
public boolean aa = true;
...
}
en B y A no se puede acceder al
campo aa a travs del nombre de la
clase A porque no es esttico.
31. Definir un campo como esttico (static).
31.1, 31.2 Los .class de los herederos y de los clientes tendrn que generarse nuevamente para actualizar
la informacin que se incrusto sobre el campo modificado (sino se hace esto, se generar una
excepcin en tiempo de ejecucin, cuando se haga uso del campo).
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 12/13
32. Adicionar o quitar la especificacin volatile a campo.
32.1, 32.2 Los clientes y herederos pueden afectarse por lo mismo que en 31.1.
33. Agregar especificacin abstract a mtodo (recuerde que todo mtodo abstracto debe estar incluido en
una clase abstracta).
33.1 Los herederos que no estn implementando el mtodo, debern ser declarados abstractos o tendrn
que redefinir el mtodo que se hizo abstracto.
abstract class A{
public void f(){...}
...
}

class A1 extends A{
...
}
haciendo el mtodo f() abstracto.

abstract class A{
public abstract void f();
...
}
la clase A1 tendr que ser declarada
abstracta o tendr que implementar el
mtodo f().
Note que una clase A1 heredera de A no puede quedar afectada, si A1 estuviese redefiniendo el mtodo
de A, que ahora se transformo a abstracto, dicha redefinicin quedara como hacer efectivo dicho mtodo.
34. Poner especificacin final a mtodo.
34.1Un heredero no puede redefinir un mtodo declarado final en un ancestro.
class A{
public void f(){...}
...
}
class A1 extends A{
public void f(){...}
...
}
haciendo final el mtodo f().
class A{
public final void f(){...}
...
}
la clase A1 no puede redefinir el mtodo
f() por ser final.
35. Hacer esttico un mtodo.
35.1 Los herederos que estn redefiniendo el mtodo que ahora es esttico, tendrn que hacer esttica la
redefinicin del mismo.
35.2 Las razones por las cules son afectados los .class de los clientes son las mismas que en 31.2.
36. Eliminar la especificacin static a mtodo.
36.1 Los herederos que redefinan este mtodo tendrn que cambiar la declaracin de la redefinicin para
no esttica.
class A{
public static void f(){...}
...
}
class A1 extends A{
public static void f(){...}
...
}
cambiando especificacin de f().
class A{
public void f(){...}
...
}
en la clase A1 el mtodo f() no puede
ser declarado esttico por que tampoco
lo es en la clase A.
36.2 Los clientes que estn haciendo uso del mtodo a travs del nombre de la clase quedan errneos.
Sobre la Implementacin.
La mayor parte de los casos anteriores son potenciales, es decir, dependen no slo del cambio efectuado
en una clase original A, sino tambin del texto de la clase B que sea cliente o extienda a A. Se ha considerado
que determinar si realmente una clase B se ve afectada por una modificacin realizada a otra A, es tan costoso
en tiempo como volver a compilar B. Es por ello que la estrategia desarrollada se limita a marcar a las
clases potencialmente errneas para exigir su recompilacin posterior. Esta marca puede ser, por
ejemplo, eliminar el archivo .class de B de modo que en una futura utilizacin de la clase B se este obligado a
generar ste .class y por tanto a recompilar a B.
Miguel Katrib Mora y Michel Estrada Ortela Compilacin Inteligente en Java 10/28/02 13/13
La deteccin de la naturaleza de una modificacin efectuada se logra comparando el .class anterior a la
compilacin de una clase con el .class resultado de la recompilacin. Este anlisis se basa en las posibilidades
de Java para hacer introspeccin (paquete java.lang.reflect del JDK 1.1
7
). Se cargan en memoria dos
objetos tipo class (a travs del mtodo forName() de la clase Class) y despus se comparan los mismos
usando a su vez las clases Constructor, Field y Method y que reflejan toda la informacin necesaria
sobre los constructores, campos y mtodos declarados en una clase (se incluyen los privados, protegidos,
pblicos y con acceso solo para el paquete), desde el tipo de los campos, los tipos de parmetros que tiene
cada mtodo, hasta los tipos de excepciones que disparan los mtodos.
La Tabla 1 se ha detallado para aclarar al lector la posible significacin de cada modificacin. Una
instrumentacin definitiva puede hacer optimizaciones. Por ejemplo, las filas 4 (adicionar un ancestro o
interface) y 5 (eliminar un ancestro o interface) ambas provocan problemas potenciales (aunque por razones
diferentes) a clientes y herederos, a los efectos de "marcar" no interesa la diferencia de estas razones, bastara
con verificar si las listas de ancestros de ambos .class (objetos class creados en el reflection) no son iguales.
Las filas 23 (cambiar de tipo a parmetro) y 24 (agregar o eliminar parmetro) tienen el efecto de eliminar
un mtodo o constructor con la vieja signatura y agregar un mtodo o constructor con la nueva signatura. El
lector puede analizar sus propias optimizaciones para disminuir la cantidad de filas de esta tabla sin prdida
de expresividad. Una vez decidida la cantidad de filas stas pueden reorganizarse segn un anlisis de: la
probabilidad de ocurrencia de las modificacin, costo de tiempo estimado para su determinacin,
trascendencia de la modificacin (por ejemplo si puede afectar a clientes, a herederos y si adems provoca
cambios en el modelo de objetos de los clientes y (o) herederos).
Conclusin
Algunas de las modificaciones aqu presentadas no son detectadas por JBuilder (filas 1,2,4,5 y 19 en la
Tabla 1). Sin embargo, el JBuilder instrumenta un algoritmo (usa el archivo con extensin dependency) para
detectar, en tiempo de compilacin; los errores que se pudieran producir en tiempo de ejecucin. Este no es el
caso de Visual J++ que detecta estos errores en compilacin, sin importarle el tiempo necesario para ello
(compilando clases innecesariamente)
8
.
La estrategia que se ha expuesto brinda la posibilidad de mejorar el proceso de compilacin de archivos
fuentes de las clases que componen una aplicacin Java. Aunque se pudieran buscar otras estrategias para
evitar la recompilacin innecesaria de estos archivos (como la usada por JBuilder) lo invariable en estas,
sera la deteccin de las modificaciones expuestas en la Tabla 1 y la necesidad de evitar los efectos que cada
modificacin provoca, al compilar solamente los archivos afectados por estas modificaciones, ganndose en
tiempo de compilacin al no compilar los archivos no afectados y en tiempo de desarrollo y robustez al
detectar mas tempranamente posibles errores.
Agradecimientos
Los autores agradecen al colega Francisco Avila, del grupo de Programacin Orientada a Objetos del
Dpto. de Ciencia de la Computacin de la Universidad de la Habana, por sus comentarios y opiniones.
Referencias.
Katrib M., Couso T., y Mateo M., Smart Compiling for Eiffel, The Journal of Object-Oriented
Programing, Vol 11, No 2, May 1998.
Gosling J., Joy B., and Steele G., Binary Compatibility, The Java Language Specification, August 1996.
Sun Microsystems , Class File Format, The Java Virtual Machine Specification, August 21, 1995.




7
Java Development Kit, paquete de herramientas y clases que brinda Sun para el trabajo con Java.
8
Los entornos de desarrollo para Java evolucionan con tal rapidez que en el momento de aparicin de este artculo esta situacin puede
haber variado.

Vous aimerez peut-être aussi