Referencias en Java II: referencias débiles
En un post anterior escribí sobre los distintos tipos de referencias a objetos que se pueden usar en Java. Hoy propongo echar mano a un poco de código y ver como se utilizan las referencias débiles, ó weak references.
El secreto de todo esto de las referencias se encuentra en el package java.lang.ref que consta de un par de clases:
+ Reference
+WeakReference
+PhantomReference
+SoftReference
+ ReferenceQueue
WeakReference es la clase que nos ocupa hoy y sirve para instrumentar a las famosas referencias débiles que mencione tiempo atras. Mirando todo mas pragmaticamente la diferencia entre estas weakreferences y las referencias fuertes radica en el momento de la creacion y el posterior acceso al objeto al que esta apunta. El siguiente codigo muestra el caso tipico entre dos objetos que se referencian mutuamente. La vista representa una pantalla de programa y Modelo algun objeto de dominio de negocio. Ambos objetos necesitan referencias al otro para implementar el patron observer que permita la comunicación correcta de estos dos componentes.
class Vista {
Modelo modelo;
public void imprimirModelo() {
System.out.println(modelo.value);
}
public void setModelo(Modelo m) {
modelo = m;
}
}
class Modelo {
int value;
WeakReference<Vista> vista; //referencia debil a la vista
public boolean existsVista() {
return this.vista.get()!=null;
}
public void actualizarVista() {
Vista miVista = this.vista.get(); //miVista es referencia fuerte a vista
//le avisa a la vista q cambio el modelo y debe actualizarse
//miVista.update();
}
public WeakReference<Vista> getVista() {
return vista;
}
public void setVista(Vista v) { //setter con weakreferences
vista = new WeakReference<Vista>(v);
}
}
La clase vista no tiene ningun comportamiento extraño, no podemos decir lo mismo de la clase Modelo. En ella vemos que la declaracion de una referencia debil se hace mediante la clase WeakReference indicando con generics la clase a la que va a apuntar esta referencia, en nuestro caso Vista.
Los accesors a este atributo son un tanto particulares, el setter recibe una referencia fuerte a Vista y que es envuelta con el constructor de WeakReference y finalmente asignada al atributo. El getter, sin embargo, devuelve un objeto de clase WeakReference. Con este objeto podemos obtener una referencia fuerte al objeto vista utilizando el metodo get().
El metodo existsVista() simplemente devuelve verdadero o falso indicandonos si existe el objeto al que apunta la referencia vista o si ya fue eliminado por el gc.
Un caso tipico de uso de estas clases, a saber:
public class Main {
public static void main(String[] args) {
Modelo m = new Modelo(); //creamos el modelo
Vista v = new Vista(); //creamos la vista
v.setModelo(m); //se aparean la vista y el modelo
m.setVista(v); //hay que recordar que setVista envuelve
//el parametro con una WeakReference
m.value = 77;
v.imprimirModelo();
//Imprime 77... como podria esperarse
v = null; //no queremos usar más la vista.
System.gc(); //pasa el basurero
System.out.println(m.existsVista());
}
}
Imprime false porque la referencia fuerte que apuntaba a la vista ya no lo hace. La unica referencia a ese objeto que queda es el atributo del modelo, que por ser una referencia debil el gc la ignora.
Recomiendo pasar estos pedazos de códigos por el amigo debugger e ir siguiendo el movimiento de las referencias y como desaparecen los objetos por arte de magia. En otro momento seguimos con las referencias fantasmas y blandas.