Funzioni annidate in Java

Esistono estensioni per il linguaggio di programmazione Java che rendono ansible la creazione di funzioni annidate? Ci sono molte situazioni in cui ho bisogno di creare metodi che vengono utilizzati solo una volta nel contesto di un altro metodo o ciclo. Non sono stato in grado di realizzare questo in Java finora, anche se può essere fatto facilmente in Javascript.

Ad esempio, questo non può essere fatto in Java standard:

for(int i = 1; i < 100; i++){ times(2); //multiply i by 2 and print i times(i); //square i and then print the result public void times(int num){ i *= num; System.out.println(i); } } 

Java 8 introduce lambda.

 java.util.function.BiConsumer times = (i, num) -> { i *= num; System.out.println(i); }; for (int i = 1; i < 100; i++) { times.accept(i, 2); //multiply i by 2 and print i times.accept(i, i); //square i and then print the result } 

La syntax () -> funziona su qualsiasi interfaccia che definisce esattamente un metodo. Quindi puoi usarlo con Runnable ma non funziona con List .

BiConsumer è una delle molte interfacce funzionali fornite da java.util.function .

Vale la pena notare che sotto il cofano, questo definisce una class anonima e lo istanzia. times è un riferimento all'istanza.

La risposta che segue sta parlando del più vicino ansible ad avere funzioni nidificate in Java prima di Java 8. Non è necessariamente il modo in cui gestirò le stesse attività che potrebbero essere gestite con funzioni annidate in Javascript. Spesso un metodo di helper privato funziona altrettanto bene – possibilmente anche un tipo di helper privato, che crea un’istanza all’interno del metodo, ma che è disponibile per tutti i metodi.

Naturalmente in Java 8 ci sono espressioni lambda che sono una soluzione molto più semplice.


Il più vicino che puoi facilmente raggiungere è con una class interiore anonima. Questo è il momento in cui Java arriva alle chiusure al momento, anche se si spera che ci sarà più supporto in Java 8.

Le classi interne anonime hanno vari limiti: sono ovviamente piuttosto prolisse rispetto al tuo esempio Javascript (o qualsiasi cosa usi lambdas) e il loro accesso all’ambiente circostante è limitato alle variabili finali.

Quindi per (orribilmente) pervertire il tuo esempio:

 interface Foo { void bar(int x); } public class Test { public static void main(String[] args) { // Hack to give us a mutable variable we can // change from the closure. final int[] mutableWrapper = { 0 }; Foo times = new Foo() { @Override public void bar(int num) { mutableWrapper[0] *= num; System.out.println(mutableWrapper[0]); } }; for (int i = 1; i < 100; i++) { mutableWrapper[0] = i; times.bar(2); i = mutableWrapper[0]; times.bar(i); i = mutableWrapper[0]; } } } 

Produzione:

 2 4 10 100 

È l'output che ottieni da Javascript?

Penso che il più vicino ansible ad avere funzioni annidate in Java 7 non sia usando una class interna anonima (la risposta di Jon Skeet sopra) ma usando le classi locali altrimenti molto raramente usate. In questo modo, nemmeno l’interfaccia della class nidificata è visibile al di fuori dell’ambito previsto ed è anche un po ‘meno prolissa.

L’esempio di Jon Skeet implementato con una class locale sarebbe il seguente:

 public class Test { public static void main(String[] args) { // Hack to give us a mutable variable we can // change from the closure. final int[] mutableWrapper = { 0 }; class Foo { public void bar(int num) { mutableWrapper[0] *= num; System.out.println(mutableWrapper[0]); } }; Foo times = new Foo(); for (int i = 1; i < 100; i++) { mutableWrapper[0] = i; times.bar(2); i = mutableWrapper[0]; times.bar(i); i = mutableWrapper[0]; } } } 

Produzione:

 2 4 10 100 

Tali metodi sono talvolta chiamati chiusure. Dai un’occhiata a Groovy , forse lo preferiresti a Java. In Java 8 probabilmente ci saranno anche chiusure (vedi JSR335 e lista differita ).

Prendi in considerazione la possibilità di creare una class locale anonima e di utilizzare il blocco di inizializzazione per eseguire il lavoro:

 public class LocalFunctionExample { public static void main(final String[] args) { for (final int i[] = new int[] { 1 }; i[0] < 100; i[0]++) { new Object() { { times(2); //multiply i by 2 and print i times(i[0]); //square i and then print the result } public void times(final int num) { i[0] *= num; System.out.println(i[0]); } }; } } } 

Produzione:

 2 4 10 100 

(Il "trucco finale del wrapper" non è automaticamente richiesto con questa tecnica, ma era necessario qui per gestire il requisito di mutazione.)

Funziona in modo quasi conciso come la versione lambda, ma puoi usare qualunque metodo firme tu voglia, ottengono nomi di parametri reali, ei metodi sono chiamati direttamente con i loro nomi - non c'è bisogno di .apply() o cosa no. (Questo tipo di cose a volte rende gli strumenti IDE un po 'più efficaci).

Odio usare la parola proibita ma potresti usare un’istruzione goto per creare una subroutine efficace all’interno del metodo. È brutto e pericoloso ma molto più facile di quanto mostrato nelle risposte precedenti. Anche se il metodo privato con una chiamata all’interno del primo metodo è molto meglio, e serve proprio bene. Non so perché vorresti usare un metodo annidato per qualcosa di così semplice.