2010-09-27 8 views
19

Stavo leggendo Javascript orientato agli oggetti e ho trovato il concetto di chiusure. Non ho capito bene perché e quando è usato. Anche altri linguaggi come Java hanno chiusure? Fondamentalmente voglio capire come conoscere il concetto di chiusure possa aiutarmi a migliorare la mia codifica.Cos'è una chiusura? Java ha chiusure?

+0

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

+0

Se qualcuno è stupido come me e se stai battendo la tua testa contro il muro solo per sapere Che cos'è l'hack questa chiusura .... poi eccoti .. https://www.youtube.com/watch?v=Nj3_DMUXEbE –

risposta

24

Una chiusura è una funzione di prima classe con variabili associate.

Circa il che significa che:

  • È possibile passare la chiusura come parametro ad altre funzioni
  • I negozi di chiusura del valore di alcune variabili dal campo di applicazione lessicale che esisteva al momento in cui è stato creato

Java inizialmente non aveva il supporto sintattico per le chiusure (queste sono state introdotte in Java 8), anche se era pratica abbastanza comune simularle usando classi interne anonime. Ecco un esempio:

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

Grazie a mikera, ciò che so capire è che la clousure è una funzione con alcune variabili che possono essere passate come parametri ad altre funzioni. Ho due domande wat è una funzione di prima classe e cosa intendi per negozi alcuni valori di scope lessicale. Inoltre, non sto ancora ottenendo uno scenario perfetto in cui sarei benevolo usandolo. Scusa, sono molto nuovo, quindi chiedere troppo –

+1

La funzione di prima classe significa che puoi trattare la funzione come faresti con qualsiasi altro "oggetto" nella lingua, cioè passarlo come parametro, memorizzarlo in una raccolta ecc. una classe interna anonima rappresenta un oggetto Java come qualsiasi altro oggetto, è "di prima classe" in Java - puoi fare qualsiasi cosa che potresti fare con qualsiasi altro oggetto normale – mikera

+1

Esempio del vantaggio è dato sopra - tu hai un funzione di ordinamento generica (ad es. Arrays.sort (..)) che diventa molto più potente perché puoi passare qualsiasi funzione di confronto che ti piace come parametro. per esempio. puoi ordinare in ordine inverso, o per numero di cifre, o se il numero è un numero primo ecc. Questo ti dà fondamentalmente un ulteriore livello di astrazione quando crei algoritmi che possono essere molto potenti. Vale la pena leggere su "programmazione funzionale" se vuoi saperne di più. – mikera

3

Una chiusura è una tecnica di scoping. Java non ha chiusure.

in javascript si può fare una cosa del genere seguente:

var scope = this; 

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

se poi fare qualcosa di simile passaggio f a una funzione, la portata è la portata in cui è stato definito.

+0

Come lo chiameresti allora quando ti riferisci a variabili dichiarate al di fuori dell'ambito della tua classe anonima? –

+1

@Kirk Una "chiusura minima". –

+0

Ho letto che la chiusura è una tecnica di scoping. Grazie, ma voglio capire come aiuta in un particolare scenario di codifica. –

9

Le chiusure sono so con vari nomi in diverse lingue, ma i punti essenziali sono i seguenti:

Per creare chiusure è necessario un linguaggio in cui il tipo di funzione è un 1 ° classe cittadino cioè può essere associato a una variabile e passato come qualsiasi vecchia stringa, int o bool.

È inoltre necessario essere in grado di dichiarare funzioni in linea. In javascript puoi fare qualcosa del genere:

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

Per passare una funzione anonima come parametro alla funzione foo. Possiamo usarlo per creare una chiusura.

Chiusure Le variabili "close over" possono essere utilizzate per passare le variabili dell'ambito. Considerate questo esempio:

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

Il lato di uova contiene ora una funzione che aggiunge "e uova" a qualunque prodotto alimentare è passato. La variabile spam fa parte dell'ambito della funzione foo e andrebbe persa quando la funzione è stata chiusa, tranne per il fatto che la chiusura "ha chiuso" lo spazio dei nomi mantenendolo finché la chiusura rimane in memoria.

Quindi siamo chiari sulle chiusure che hanno accesso alle variabili private con scope dei loro genitori giusto? Che ne dici di usarli per simulare i modificatori di accesso privati ​​in 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" 

Quindi quello che sta succedendo qui è che stiamo creando e chiamando immediatamente una funzione anonima. C'è una variabile privata nella funzione. Restituisce un oggetto con un singolo metodo che fa riferimento a questa variabile. Una volta che la funzione è terminata, il metodo getConstant è l'unico modo per accedere alla variabile. Anche se questo metodo viene rimosso o sostituito, non rinuncerà al suo segreto. Abbiamo usato chiusure per ottenere l'incapsulamento e il nascondiglio variabile. Per una spiegazione più approfondita vedi http://javascript.crockford.com/private.html

Java non ha ancora chiusure. Il più vicino che ha sono classi interne anonime. Tuttavia per istanziare uno di questi elementi in linea devi istanziare un intero oggetto (di solito da un'interfaccia esistente). La bellezza delle chiusure è che racchiudono affermazioni semplici ed espressive che sono un po 'perse nel rumore delle classi interne anonime.

+0

Da Wikipedia: "Il termine chiusura è spesso usato erroneamente per indicare la funzione anonima, probabilmente perché la maggior parte dei linguaggi che implementano funzioni anonime consente loro di formare chiusure e i programmatori vengono di solito introdotti a entrambi i concetti contemporaneamente. concetti ". –

+0

@Kirk Woll fair point anche se non era mia intenzione equiparare i due come identici. Proverò a renderlo un po 'più chiaro. –

+0

mi piace la tua spiegazione. Puoi dare un esempio più pratico in cui ha perfettamente senso usare la chiusura è stato il modo perfetto per risolvere un problema –

3

La chiusura è una funzionalità molto naturale che consente di catturare variabili libere dal proprio ambiente lessicale.

Ecco un esempio in JavaScript:

function x() { 

    var y = "apple"; 

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

funzione x restituisce una funzione.si noti che quando una funzione viene creata le variabili utilizzate in questa funzione non vengono valutate come quando restituiamo un'espressione. quando viene creata la funzione, guarda per vedere quali variabili non sono locali alla funzione (gratuite). Individua quindi queste variabili libere e garantisce che non siano raccolte in modo da poter essere utilizzate una volta che la funzione viene effettivamente chiamata.

Per supportare questa funzione è necessario disporre di funzioni di prima classe che java non supporta.

Nota questo è un modo in cui possiamo avere variabili private in linguaggi come JavaScript.

+3

Vendo "molto naturale". La stessa domanda sottolinea che è arcano per molti. –

+0

ci sono applicazioni in cui è davvero naturale. per esempio quando si passano le funzioni di callback e quando si lavora in linguaggi funzionali. Solo perché è arcano non significa che non sia una buona idea. : P –

+0

Oh, non metto in dubbio la loro utilità, tutt'altro. Solo dicendo che ci è voluto del tempo per avvolgere la mia mente intorno a loro, per me. –

6

Mentre Java non ha funzioni di prima classe, in realtà ha le chiusure lessicali.

Per esempio, la seguente funzione Lisp (rubato dal libro di Paul Graham On Lisp) restituisce una funzione che aggiunge un numero:

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

questo può essere fatto in Java. Tuttavia, poiché non ha funzioni di prima classe, dobbiamo definire un'interfaccia (chiamiamola Adder) e una classe interna anonima con una funzione che implementa questa interfaccia.

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; 
     } 
    }; 
} 

L'interno add() funzione è una chiusura lessicale perché utilizza la variabile n dall'ambito lessicale esterno.

La variabile doveva essere dichiarata final per fare ciò, il che significa che la variabile non può cambiare. Tuttavia, è possibile modificare i valori all'interno delle variabili di riferimento, anche se sono definitivi. Ad esempio, si consideri la seguente funzione Lisp (anche da On Lisp):

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

Ciò può essere implementato in Java avvolgendo la variabile esterna in una variabile di tipo riferimento, ad esempio una matrice o un oggetto.

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; 
      } 
     } 
    }; 
} 

Proverò che questa è una vera chiusura lessicale, non una simulazione. Ma non dirò che è carina.