Java7 Closures

Al votar hoy en la encuesta de JavaHispano.org acerca de los closures en Java 7 pude ver que los resultados reflejaban que una gran cantidad de personas registradas en este sitio no conocen los closures. La verdad es que cualquier programador que haya trabajado con otros lenguajes como Lisp, JavaScript o Ruby conocerá los closures decerca. Incluso Groovy, tiene closures.

Funciones-objeto y closures

Lo primero que hay que entender es la diferencia entre funciones como objetos y closures. Una función puede ser anónima o definida, pero también pueden ser objetos en si mismas. Esto nos permite utilizar funciones como argumentos, ser devueltas por otras funciones o ser almacenadas en estructuras de datos. Vamos, son objetos de primera clase o de primer orden.
Un closure es una función más su entorno y estos pueden ser de igual forma tratados como objetos de primera clase, pero al contener un entorno, podemos acceder a variables (o funciones) de este entorno incluso cuando el mismo no este activo. Existen lenguajes que soportan funciones-objetos o closures o ambos, y muchos de los lenguajes funcionales en su mayoria utilizan funciones y funciones-closure.
Entonces, un closure, por ejemplo, nos permite definir una función que tiene acceso a una variable, así que podemos almacenar esta funcion en un objeto y acceder a ella. El contexto de la función no se limita sólo a los objetos a los que tiene acceso directo, pero también a las variables que obtiene vía el entorno.

¿Cómo encaja esto dentro de Java? 

Veamos un ejemplo clásico de sort con un método anónimo:
  1. anArray.sort(new Comparator() {
  2.     public int compare(MyClass a, MyClass b) {
  3.         return a.myProperty().compareTo(b.myProperty();
  4.     }
  5. });
Con closures haríamos esto:



  1. anArray.sort( (a, b) => a.myProperty().compareTo(b.myProperty() );
Es algo muy usual realizar operaciones sobre cada objeto de una lista, por ejemplo:

  1. public List squareIntegers( List iList) {
  2.     List result = new ArrayList(iList.size());
  3.     for (Integer i : iList){
  4.        result.add(i*i);
  5.     }
  6.     return result;
  7. }
  8. iList = squareIntegers(iList);
Con un closure:

  1. iList = iList.map( { Integer i => i*i } );
Pero estas son realmente funciones-objetos que son pasadas como argumentos a otras funciones. Así que una
definición teórica de closure, es decir, un método que puede obtener o hacer "close over" sobre el contexto del código al que pertenece sería:

  1. public Collection getSpanishDvd( Collection dvds ){
  2.     return select( dvds, { Dvd aDvd => dvd.isSpanish() } );
  3. }

  4. public static Collection select(Collection source, {T=>Boolean} predicate) {
  5.     Collection result = new ArrayList();
  6.     for (T o : source) {
  7.         if (predicate.invoke(o)) result.add(o);
  8.     }
  9. }

¿Son utiles los closures?

En lenguajes como Python llevan mucho tiempo discutiendo sobre la necesidad de lambda y la verdad es que IMO no son algo necesario, pero que es bueno tener a mano para cuando las circunstancias lo precisen. Este tema es parecido al de los for-each con Java6 donde muchas personas desconfiaban de la implementación de estos y de su utilidad. Como sucede con cualquier herramienta, es mejor entenderla antes de utilizarla para no terminar con código spaghetti que dificulte la lectura y el mantenimiento.

Os recomiendo leer el libro SICP para comprender los closures en su totalidad y además un poco de Lisp.