2010-09-27 14 views
19

Estaba leyendo Javascript orientado a objetos y encontré el concepto de cierres. No entendí bien por qué y cuándo se usa. ¿Otros lenguajes como Java también tienen cierres? Básicamente, quiero entender cómo conocer el concepto de cierres puede ayudarme a mejorar mi codificación.¿Qué es un cierre? ¿Java tiene cierres?

+0

Relacionados: http://stackoverflow.com/questions/795549/difference-between-classjava-and-closurejavascript –

+0

Si alguien es tonto como yo y si te golpeas la cabeza contra la pared solo para saber qué es el truco este cierre ... luego aquí tienes ... https://www.youtube.com/watch?v=Nj3_DMUXEbE –

Respuesta

24

Un cierre es una función de primera clase con variables enlazadas.

Aproximadamente eso significa que:

  • puede pasar el cierre como un parámetro para otras funciones
  • Las tiendas de cierre el valor de algunas variables del ámbito léxico que existía en el momento en que se ha creado

En un principio, Java no tenía soporte sintáctico para cierres (estos se introdujeron en Java 8), aunque era una práctica bastante común simularlos utilizando clases internas anónimas. Aquí hay un ejemplo:

import java.util.Arrays; 
import java.util.Comparator; 

public class StupidComparator { 
    public static void main(String[] args) { 
     // this is a value used (bound) by the inner class 
     // note that it needs to be "final" 
     final int numberToCompareTo=10; 

     // this is an inner class that acts like a closure and uses one bound value 
     Comparator<Integer> comp=new Comparator<Integer>() { 
      public int compare(Integer a, Integer b) { 
       int result=0; 
       if (a<numberToCompareTo) result=result-1; 
       if (b<numberToCompareTo) result=result+1; 
       return result; 
      } 
     }; 

     Integer[] array=new Integer[] {1,10, 5 , 15, 6 , 20, 21, 3, 7}; 

     // this is a function call that takes the inner class "closure" as a parameter 
     Arrays.sort(array,comp); 

     for (int i:array) System.out.println(i); 
    } 
} 
+0

Gracias mikera, lo que sé entender es que el cierre es una función con algunas variables que se pueden pasar como parámetros a otras funciones. Tengo dos preguntas: wat es una función de primera clase y ¿a qué te refieres con almacenar algunos valores del alcance léxico? Además, todavía no estoy obteniendo un escenario perfecto en el que me beneficie usarlo. Lo siento, soy muy nuevo, por lo que pedir demasiado –

+1

La función de primera clase significa que puedes tratar la función como lo harías con cualquier otro "objeto" en el idioma, es decir, pasarla como parámetro, almacenarla en una colección, etc. una clase interna anónima representa un objeto Java al igual que cualquier otro objeto, es "primera clase" en Java; puede hacer cualquier cosa que pueda hacer con cualquier otro objeto normal – mikera

+1

Ejemplo del beneficio que se da arriba: tiene una La función de clasificación genérica (por ejemplo, Arrays.sort (..)) se vuelve mucho más poderosa porque puede pasar cualquier función de comparación que desee como parámetro. p.ej. puede ordenar en orden inverso, o por número de dígitos, o por si el número es primo, etc. Esto básicamente le da una capa extra de abstracción cuando crea algoritmos que pueden ser muy poderosos. Vale la pena leer sobre la "programación funcional" si quieres saber más. – mikera

3

Un cierre es una técnica de alcance. Java no tiene cierres.

en JavaScript que puede hacer algo por el estilo siguiente:

var scope = this; 

var f = function() { 
    scope.somethingOnScope //scope is 'closed in' 
} 

si luego hacer algo como pase a una función f, el alcance es el alcance de donde se define.

+0

¿Cómo lo llamarías entonces cuando te refieres a variables declaradas fuera del alcance de tu clase anónima? –

+1

@Kirk Un "cierre mínimo". –

+0

Lo he leído que el cierre es una técnica de scoping. Gracias, pero quiero entender cómo ayuda con un escenario de codificación particular. –

9

Los cierres son conocidas por diferentes nombres en diferentes idiomas, pero los puntos esenciales son los siguientes:

Para crear cierres necesita un lenguaje donde el tipo de función es una primera clase ciudadano es decir, puede estar unido a una variable y transmitido como cualquier cadena anterior, int o bool.

También necesita poder declarar las funciones en línea. En javascript que puede hacer algo como esto:

foo("bar", "baz" function(x){alert("x")}); 

para aprobar una función anónima como parámetro a la función foo. Podemos usar esto para crear un cierre.

Cierran las variables de "cierre sobre", por lo que se pueden usar para pasar variables de ámbito. Considere este ejemplo:

function foo(){ 
    var spam = " and eggs"; 
    return function(food){alert(food + spam)}; 
} 
var sideOfEggs = foo(); 

El lado de los huevos contiene ahora una función que añade "y" huevos a lo que los productos alimenticios que se pasa. La variable de spam es parte del alcance de la función foo y se habría perdido al salir de la función, excepto que el cierre "cerró" el espacio de nombre y lo mantuvo mientras el cierre permanezca en la memoria.

Así que tenemos claro que los cierres tienen acceso a las variables de ámbito privado privadas de sus padres ¿no? Entonces, ¿qué hay de usarlos para simular modificadores de acceso privado en javascript?

var module = (function() { 
    var constant = "I can not be changed"; 

    return { 
     getConstant : function() { //This is the closure 
      return constant;    //We're exposing an otherwise hidden variable here 
     } 
    }; 
}());          //note the function is being defined then called straight away 

module.getConstant();      //returns "I can not be changed" 
module.constant = "I change you!"; 
module.getConstant();      //still returns "I can not be changed" 

Entonces, lo que está sucediendo aquí es que estamos creando e llamando inmediatamente a una función anónima. Hay una variable privada en la función. Devuelve un objeto con un único método que hace referencia a esta variable. Una vez que la función ha salido, el método getConstant es la única forma de acceder a la variable. Incluso si este método se elimina o reemplaza, no se perderá su secreto. Hemos utilizado cierres para lograr la encapsulación y la ocultación variable. Para una explicación más detallada de esto, vea http://javascript.crockford.com/private.html

Java no tiene cierres (todavía). Lo más cercano que tiene son clases internas anónimas. Sin embargo, para instanciar uno de estos en línea, debe instanciar un objeto completo (generalmente desde una interfaz existente). La belleza de los cierres es que encapsulan enunciados simples y expresivos que se pierden un tanto en el ruido de las clases internas anónimas.

+0

De Wikipedia: "El término cierre a menudo se usa erróneamente para referirse a la función anónima. Esto se debe probablemente a que la mayoría de los lenguajes que implementan funciones anónimas les permiten formar cierres y los programadores generalmente se presentan a ambos conceptos al mismo tiempo. conceptos." –

+0

@Kirk Woll fair point aunque no era mi intención igualar los dos como idénticos. Trataré de aclararlo un poco. –

+0

me gusta su explicación. ¿Puede dar un ejemplo más práctico en el que tiene mucho sentido si el uso del cierre fue la manera perfecta de resolver un problema –

3

El cierre es una característica muy natural que permite que las variables libres sean capturadas por su entorno léxico.

He aquí un ejemplo en javascript:

function x() { 

    var y = "apple"; 

    return (function() { 
     return y; 
    }); 
} 

función x devuelve una función.tenga en cuenta que cuando se crea una función, las variables utilizadas en esta función no se evalúan como cuando devolvemos una expresión. cuando se crea la función, busca ver qué variables no son locales para la función (gratuita). A continuación, ubica estas variables gratuitas y garantiza que no se recojan elementos no utilizados para que puedan utilizarse una vez que se llame a la función.

Para admitir esta función necesita tener funciones de primera clase que java no admite.

Tenga en cuenta que esta es una forma en que podemos tener variables privadas en idiomas como JavaScript.

+3

FSVO "muy natural". La misma pregunta señala que es arcano para muchos. –

+0

hay aplicaciones donde es realmente natural. por ejemplo, al pasar por las funciones de devolución de llamada, y cuando está trabajando en lenguajes funcionales. El hecho de que sea arcano no significa que no sea una buena idea. : P –

+0

Oh, no cuestiono su utilidad, ni mucho menos. Solo digo que tomar un tiempo para envolver mi cabeza, para mí. –

6

Si bien Java no tiene funciones de primera clase, de hecho tiene cierres léxicos.

Por ejemplo, la siguiente función Lisp (robado de libro de Paul Graham En Lisp) devuelve una función que añade un número:

(defun make-adder (n) 
    (lambda (x) (+ x n)) 

Esto se puede hacer en Java. Sin embargo, dado que no tiene funciones de primera clase, necesitamos definir una interfaz (llamémoslo Adder) y una clase interna anónima con una función que implemente esta interfaz.

public interface Adder { 
    int add(int x); 
} 

public static Adder makeAdder(final int n) { 
    return new Adder() { 
     public int add(int x) { 
      return x + n; 
     } 
    }; 
} 

El interior add() función es un cierre de léxico, ya que utiliza la variable n del alcance léxico exterior.

La variable tuvo que ser declarada final para hacer esto, lo que significa que la variable no puede cambiar. Sin embargo, es posible cambiar los valores dentro de las variables de referencia, incluso si son finales. Por ejemplo, considere la siguiente función Lisp (también de On Lisp):

(defun make-adderb (n) 
    (lambda (x &optional change) 
    (if change 
     (setq n x) 
     (+ n n)))) 

Esto puede ser implementado en Java envolviendo la variable exterior en una variable de tipo de referencia, tal como una matriz o un objeto.

public interface MutableAdder { 
    int add(int x, boolean change); 
} 

public static MutableAdder makeAdderB(int n) { 
    final int[] intHolder = new int[] { n }; 
    return new MutableAdder() { 
     public int add(int x, boolean change) { 
      if (change) { 
       intHolder[0] = x; 
       return x; 
      } 
      else { 
       return intHolder[0] + x; 
      } 
     } 
    }; 
} 

Voy a afirmar que se trata de cierres reales léxicos, no una simulación. Pero no reclamaré que sea bonita.