closure java closures

closure - ¿Necesita Java cierres?



closure javascript (19)

Últimamente he estado leyendo mucho sobre la próxima versión de Java que posiblemente respalda cierres . Siento que tengo una idea bastante clara de lo que son los cierres, pero no puedo pensar en un ejemplo sólido de cómo mejorarían "un lenguaje orientado a objetos". ¿Alguien puede darme un caso de uso específico donde se necesitaría un cierre (o incluso se preferiría)?


Hay algunas "funciones de orden superior" muy útiles que pueden hacer operaciones en listas usando cierres. Las funciones de orden superior son funciones que tienen ''objetos de función'' como parámetros.

Por ejemplo, es una operación muy común aplicar alguna transformación a cada elemento de una lista. Esta función de orden superior se denomina comúnmente "mapa" o "recopilar". (Ver el operador *. Spread de Groovy ).

Por ejemplo, para cuadrar cada elemento en una lista sin cierres, probablemente escribirías:

List<Integer> squareInts(List<Integer> is){ List<Integer> result = new ArrayList<Integer>(is.size()); for (Integer i:is) result.add(i*i); return result; }

Usando cierres y mapa y la sintaxis propuesta, podrías escribirlo así:

is.map({Integer i => i*i})

(Aquí hay un posible problema de rendimiento con respecto al boxeo de tipos primitivos).

Como explicó Pop Catalin, hay otra función de orden superior llamada ''seleccionar'' o ''filtro'': se puede usar para obtener todos los elementos en una lista que cumpla con alguna condición. Por ejemplo:

En lugar de:

void onlyStringsWithMoreThan4Chars(List<String> strings){ List<String> result = new ArrayList<String>(str.size()); // should be enough for (String str:strings) if (str.length() > 4) result.add(str); return result; }

En cambio, podrías escribir algo como

strings.select({String str => str.length() > 4});

usando la propuesta.

Puede ver la sintaxis de Groovy, que es una extensión del lenguaje Java para admitir cierres en este momento. Consulte el capítulo sobre colecciones de la Guía del usuario de Groovy para ver más ejemplos de qué hacer con los cierres.

Una observación:

Tal vez se necesite alguna aclaración con respecto al término "cierre". Lo que he mostrado arriba se habla estrictamente sin cierres. Son solo ''objetos funcionales''. Un cierre es todo lo que puede capturar o "cerrar" el contexto (léxico) del código que lo rodea. En ese sentido, hay cierres en Java en este momento, es decir, clases anónimas:

Runnable createStringPrintingRunnable(final String str){ return new Runnable(){ public void run(){ System.out.println(str); // this accesses a variable from an outer scope } }; }


Como un desarrollador de Java que está tratando de aprender a sí mismo con ceceo en un intento de convertirse en un mejor programador, diría que me gustaría ver implementada la propuesta de Josh Block para cierres. Me encuentro utilizando clases internas anónimas para expresar cosas como qué hacer con cada elemento de una lista al agregar algunos datos. Sería bueno representar eso como un cierre, en lugar de tener que crear una clase abstracta.


Java ha tenido cierres desde 1.1, solo de una manera muy engorrosa y limitada.

A menudo son útiles donde sea que tenga una devolución de llamada de alguna descripción. Un caso común es abstraer el flujo de control, dejando el código interesante para llamar a un algoritmo con un cierre que no tiene flujo de control externo.

Un ejemplo trivial es para cada uno (aunque Java 1.5 ya lo tiene). Si bien puede implementar un método para cada método en Java tal como está, es demasiado detallado para ser útil.

Un ejemplo que ya tiene sentido con Java existente es la implementación del modismo "ejecutar alrededor", mediante el cual se abstrae la adquisición y liberación de recursos. Por ejemplo, el archivo abierto y cerrado se puede hacer dentro de try / finally, sin que el código del cliente tenga que obtener los detalles correctos.


Lo más obvio sería un pseudo-reemplazo para todas aquellas clases que solo tienen un único método llamado run () o actionPerformed () o algo así. Entonces, en lugar de crear un subproceso con un Runnable incrustado, utilizaría un cierre en su lugar. No más poderoso que lo que tenemos ahora, pero mucho más conveniente y conciso.

Entonces, ¿ necesitamos cierres? No. ¿Sería bueno tenerlos? Claro, mientras no se sientan atrapados, como me temo que lo estarían.


No mejoran el lenguaje orientado a objetos. Hacen que los lenguajes prácticos sean más prácticos.

Si estás atacando un problema con el martillo OO, representa todo como interacciones entre objetos, entonces un cierre no tiene sentido. En un lenguaje OO basado en la clase, los cierres son las salas traseras llenas de humo donde se hacen las cosas pero nadie habla de ellas después. Conceptualmente, es aborrecible.

En la práctica, es extremadamente conveniente. Realmente no quiero definir un nuevo tipo de objeto para mantener el contexto, establecer el método "hacer cosas" para él, crear instancias y poblar el contexto ... solo quiero decirle al compilador, "mira, mira qué tengo acceso a este momento, ese es el contexto que quiero, y aquí está el código para el que quiero usarlo, agárrenme esto hasta que lo necesite ".

Cosas fantásticas



Supongo que para apoyar conceptos básicos de programación funcional, necesitas cierres. Hace que el código sea más elegante y composable con el soporte para cierres . Además, me gusta la idea de pasar líneas de código como parámetros para las funciones.


Java no necesita cierres, un lenguaje orientado a objetos puede hacer todo lo que hace un cierre utilizando objetos intermedios para almacenar estados o realizar acciones (en el caso de las clases internas de Java). Pero los cierres son deseables como característica porque simplifican en gran medida el código y aumentan la legibilidad y, en consecuencia, la mantenibilidad del código.

No soy especialista en Java pero estoy usando C # 3.5 y los cierres son una de mis características favoritas del lenguaje, por ejemplo, tome la siguiente declaración como ejemplo:

// Example #1 with closures public IList<Customer> GetFilteredCustomerList(string filter) { //Here a closure is created around the filter parameter return Customers.Where( c => c.Name.Contains(filter)).ToList(); }

ahora tome un ejemplo equivalente que no use cierres

//Example #2 without closures, using just basic OO techniques public IList<Customer> GetFilteredCustomerList(string filter) { return new Customers.Where( new CustomerNameFiltrator(filter)); } ... public class CustomerNameFiltrator : IFilter<Customer> { private string _filter; public CustomerNameFiltrator(string filter) { _filter = filter; } public bool Filter(Customer customer) { return customer.Name.Contains( _filter); } }

Sé que esto es C # y no Java, pero la idea es la misma, los cierres son útiles para la concisión y hacen que el código sea más corto y más legible. Detrás de escena, los cierres de C # 3.5 hacen algo que se ve muy similar al ejemplo # 2, lo que significa que el compilador crea una clase privada detrás de escena y le pasa el parámetro ''filtro''.

Java no necesita cierres para funcionar, como desarrollador tampoco los necesita, pero son útiles y brindan beneficios, lo que significa que son deseables en un lenguaje que es un lenguaje de producción y uno de sus objetivos es la productividad. .


Cuando los cierres finalmente lleguen a Java, me alegraré alegremente de todas mis clases personalizadas de comparadores.

myArray.sort( (a, b) => a.myProperty().compareTo(b.myProperty() );

... se ve muchísimo mejor que ...

myArray.sort(new Comparator<MyClass>() { public int compare(MyClass a, MyClass b) { return a.myProperty().compareTo(b.myProperty(); } });


Últimamente he estado leyendo mucho sobre la próxima versión de Java que posiblemente respalda cierres. Siento que tengo una idea bastante clara de lo que son los cierres, pero no puedo pensar en un ejemplo sólido de cómo "mejorarían" un lenguaje orientado a objetos.

Bueno, la mayoría de las personas que usan el término "cierre" realmente significan "objeto de función", y en este sentido, los objetos de función hacen posible escribir código más simple en ciertas circunstancias, como cuando necesita comparadores personalizados en una función de ordenamiento.

Por ejemplo, en Python:

def reversecmp(x, y): return y - x a = [4, 2, 5, 9, 11] a.sort(cmp=reversecmp)

Esto ordena la lista a en orden inverso pasando el functoin de comparación personalizado reversecmp. La adición del operador lambda hace que las cosas sean aún más compactas:

a = [4, 2, 5, 9, 11] a.sort(cmp=lambda x, y : y - x)

Java no tiene objetos de función, por lo que usa "clases de functor" para simularlos. En Java, realiza la operación equivalente implementando una versión personalizada de la clase Comparator y pasándola a la función de clasificación:

class ReverseComparator implements Comparator { public compare(Object x, Object y) { return (Integer) y - (Integer) x; } ... List<Integer> a = Arrays.asList(4, 2, 5, 9, 11); Collections.sort(a, new ReverseComparator());

Como puede ver, da el mismo efecto que los cierres, pero es más torpe y más detallado. Sin embargo, la adición de clases internas anónimas obvia la mayor parte del dolor:

List<Integer> a = Arrays.asList(4, 2, 5, 9, 11); Comparator reverse = new Comparator() { public Compare(Object x, Object y) { return (Integer) y - (Integer) x; } } Collections.sort(a, reverse);

Por lo tanto, diría que la combinación de clases de functor + clases internas anónimas en Java es suficiente para compensar la falta de objetos de función verdadera, por lo que su adición es innecesaria.


Los cierres en un lenguaje imperativo (ejemplos: JavaScript, C #, la próxima actualización de C ++) no son lo mismo que las clases internas anónimas. Deben poder capturar referencias modificables a variables locales. Las clases internas de Java solo pueden capturar variables final locales.

Casi cualquier característica del lenguaje puede ser criticada como no esencial:

  • for , while , do es solo azúcar sintáctico sobre goto / if .
  • Las clases internas son azúcar sintáctico sobre las clases con un campo que apunta a la clase externa.
  • Los genéricos son azúcar sintáctico sobre yesos.

Exactamente el mismo argumento "no esencial" debería haber bloqueado la inclusión de todas las características anteriores.


No solo ese benjismith, sino que me encanta cómo puedes hacer ...

myArray.sort {it.myProperty}

Solo necesita el comparador más detallado que haya mostrado cuando la comparación de la propiedad con el lenguaje natural no se ajuste a sus necesidades.

Me encanta esta característica.


Algunas personas han dicho, o implícito, que los cierres son simplemente azúcar sintáctico, haciendo lo que ya se puede hacer con clases internas anónimas y haciendo que sea más conveniente pasar parámetros.

Son azúcar sintáctico en el mismo sentido en que Java es azúcar sintáctica para el ensamblador (ese "ensamblador" podría ser bytecode, en aras del argumento). En otras palabras, elevan su nivel de abstracción, y este es un concepto importante.

Los cierres promueven el concepto de la función como objeto de una entidad de primera clase, una que aumenta la expresividad del código, en lugar de saturarlo con más repeticiones repetitivas.

Un ejemplo que está cerca de mi corazón ya ha sido mencionado por Tom Hawtin, implementando el modismo Execute Around, que es casi la única forma de llevar RAII a Java. Escribí una entrada en el blog sobre exactamente ese tema hace un par de años cuando escuché por primera vez el cierre.

Irónicamente, creo que la razón por la que los cierres serían buenos para Java (más expresividad con menos código) puede ser lo que conmueve a muchos defensores de Java. Java tiene una mentalidad de "deletrear todo por el camino largo". Eso y el hecho de que los cierres son un guiño a una forma más funcional de hacer las cosas, que también veo como algo bueno, pero pueden diluir el mensaje de OO puro que muchos en la comunidad de Java aprecian.


¿Qué sucede con la legibilidad y la mantenibilidad? Los cierres de una sola línea son más difíciles de comprender y depurar, el software tiene una vida útil y se puede obtener gente con conocimiento rudimentario del idioma para mantenerlo ... Así que extienda la lógica mejor que una sola frase para un mantenimiento sencillo ... Generalmente no tiene una estrella de software cuidando el software después de su lanzamiento ...


Es posible que desee ver Groovy, un lenguaje que es principalmente compatible con Java, y se ejecuta en el JRE, pero admite cierres.


He estado pensando mucho sobre el tema de esta pregunta tan interesante en los últimos días. En primer lugar, si he entendido bien, Java ya tiene una noción básica de cierres (definida a través de clases anónimas), pero la nueva característica que se va a presentar es la compatibilidad con cierres basados ​​en funciones anónimas.

Esta extensión definitivamente hará que el lenguaje sea más expresivo, pero no estoy seguro si realmente se ajusta al resto del lenguaje. Java se ha diseñado como un lenguaje orientado a objetos sin soporte para programación funcional: ¿será fácil de entender la nueva semántica? Java 6 ni siquiera tiene funciones, ¿tendrá Java 7 funciones anónimas pero no funciones "normales"?

Mi impresión es que a medida que los nuevos estilos de programación o paradigmas como la programación funcional se vuelven más populares, todos quieren usarlos en su lenguaje OOP favorito. Esto es comprensible: uno quiere continuar usando un idioma con el que está familiarizado mientras adopta nuevas funciones. Pero de esta forma un lenguaje puede volverse realmente complejo y perder coherencia.

Así que mi actitud en este momento es apegarme a Java 6 para OOP (espero que Java 6 siga siendo compatible por un tiempo) y, en caso de que realmente me interese hacer OOP + FP, echar un vistazo a algún otro lenguaje como Scala (Scala se definió como multi-paradigma desde el principio y puede integrarse bien con Java) en lugar de cambiar a Java 7.

Creo que Java debe su éxito al hecho de que combina un lenguaje simple con bibliotecas y herramientas muy potentes, y no creo que nuevas características como los cierres lo conviertan en un mejor lenguaje de programación.


Ahora que JDK8 está a punto de ser lanzado, hay más información disponible que puede enriquecer las respuestas a esta pregunta.

Bria Goetz, arquitecto de idiomas en Oracle, ha publicado una serie de documentos (aún borradores) sobre el estado actual de las expresiones lambda en Java. También cubre los cierres, ya que están planificando lanzarlos en el próximo JDK, que debería estar completo para enero de 2013 y debería lanzarse a mediados de 2013.


La falta de vinculación en la función anónima [es decir, si las variables (y los argumentos del método si hay un método adjunto) del contexto externo se declaran definitivas y están disponibles, pero no de otra forma], no entiendo muy bien qué es lo que la restricción realmente compra .

Utilizo "final" profusamente de todos modos. Entonces, si mi intención era usar los mismos objetos dentro del cierre, de hecho declararía que esos objetos son definitivos en el alcance adjunto. Pero lo que estaría mal al dejar que el "cierre [java aic]" obtenga una copia de la referencia como si hubiera pasado a través de un constructor (bueno, de hecho, así es como se hace).

Si el cierre quiere sobrescribir la referencia, que así sea; lo hará sin cambiar la copia que ve el alcance adjunto.

Si sostenemos que eso llevaría a un código ilegible (por ejemplo, tal vez no es sencillo ver cuál es la referencia del objeto en el momento de la llamada del constructor para el aic), entonces ¿qué tal al menos hacer que la sintaxis sea menos detallada? Scala? Groovy?


Como programador de Lisp, me gustaría que la comunidad de Java entienda la siguiente diferencia: funciona como objetos frente a cierres .

a) las funciones pueden ser nombradas o anónimas . Pero también pueden ser objetos de ellos mismos. Esto permite que las funciones pasen como argumentos, devueltos de funciones o almacenados en estructuras de datos. Esto significa que las funciones son objetos de primera clase en un lenguaje de programación.

Las funciones anónimas no le agregan mucho al idioma, solo le permiten escribir funciones de una manera más breve.

b) Un cierre es una función más un entorno vinculante . Los cierres se pueden pasar hacia abajo (como parámetros) o regresar hacia arriba (como valores de retorno). Esto permite que la función se refiera a variables de su entorno, incluso si el código circundante ya no está activo.

Si tiene a) en algún idioma, entonces surge la pregunta ¿qué hacer con b) ? Hay idiomas que tienen a) , pero no b) . En el mundo de la programación funcional a) (funciones) yb (funciones como cierres) es hoy en día la norma. Smalltalk tenía a) (los bloques son funciones anónimas) durante mucho tiempo, pero luego algunos dialectos de Smalltalk añadieron soporte para b) (bloques como cierres).

Puede imaginarse que obtiene un modelo de programación ligeramente diferente si agrega funciones y cierres al idioma.

Desde una vista pragmática, la función anónima agrega una notación breve, donde pasa o invoca funciones. Eso puede ser algo bueno.

El cierre (función más vinculación) le permite, por ejemplo, crear una función que tenga acceso a algunas variables (por ejemplo, a un valor de contador). Ahora puede almacenar esa función en un objeto, acceder a ella e invocarla. El contexto para el objeto función ahora no es solo los objetos a los que tiene acceso, sino también las variables a las que tiene acceso mediante enlaces. Esto también es útil, pero puede ver que los enlaces variables frente al acceso a las variables de objeto ahora son un problema: cuándo debería ser algo una variable léxica (a la que se puede acceder en un cierre) y cuándo debería ser una variable de algún objeto ( una ranura ). ¿Cuándo debería ser algo un cierre o un objeto? Puede usar ambos de maneras similares. Un ejercicio de programación habitual para los estudiantes que aprenden Scheme (un dialecto Lisp) es escribir un sistema de objetos simple utilizando cierres.

El resultado es un lenguaje de programación más complicado y un modelo de tiempo de ejecución más complicado. ¿Demasiado complicado?