Funciones anidadas en Java

¿Hay alguna extensión para el lenguaje de progtwigción Java que permita crear funciones anidadas? Hay muchas situaciones en las que necesito crear métodos que solo se usan una vez en el contexto de otro método o for-loop. Hasta ahora no he podido lograr esto en Java, aunque se puede hacer fácilmente en Javascript.

Por ejemplo, esto no se puede hacer en Java estándar:

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 presenta lambdas.

 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 () -> funciona en cualquier interfaz que defina exactamente un método. Entonces puedes usarlo con Runnable pero no funciona con List .

BiConsumer es una de las muchas interfaces funcionales proporcionadas por java.util.function .

Vale la pena señalar que bajo el capó, esto define una clase anónima y lo ejemplifica. times es una referencia a la instancia.

La respuesta a continuación es hablar de lo más cerca que puede llegar a tener funciones anidadas en Java antes que Java 8. No es necesariamente la forma en que manejaría las mismas tareas que podrían manejarse con funciones anidadas en Javascript. A menudo, un método de ayuda privada funcionará igual de bien, posiblemente incluso un tipo de ayuda privada, del que se crea una instancia dentro del método, pero que está disponible para todos los métodos.

En Java 8, por supuesto, hay expresiones lambda que son una solución mucho más simple.


Lo más cerca que puede venir fácilmente es con una clase interna anónima. Eso es lo más cercano que Java llega a los cierres en este momento, aunque es de esperar que haya más soporte en Java 8.

Las clases internas anónimas tienen varias limitaciones: obviamente son bastante verbosas en comparación con su ejemplo de JavaScript (o cualquier cosa que use lambdas) y su acceso al entorno envolvente está limitado a las variables finales.

Entonces (horriblemente) pervertir tu ejemplo:

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

Salida:

 2 4 10 100 

¿Es ese el resultado que obtienes del Javascript?

Creo que lo más cercano que se puede llegar a tener funciones anidadas en Java 7 no es mediante el uso de una clase interna anónima (respuesta de Jon Skeet anterior), sino mediante el uso de las clases locales muy raramente utilizados. De esta manera, ni siquiera la interfaz de la clase anidada es visible fuera del scope previsto y también es un poco menos prolija.

El ejemplo de Jon Skeet implementado con una clase local se vería de la siguiente manera:

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

Salida:

 2 4 10 100 

Tales métodos a veces se llaman cierres. Eche un vistazo a Groovy , tal vez lo prefiera a Java. En Java 8 probablemente también habrá cierres (ver JSR335 y lista diferida ).

Considere hacer una clase local anónima y usar su bloque inicializador para hacer el trabajo:

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

Salida:

 2 4 10 100 

(El "truco de envoltura final" no se requiere automáticamente con esta técnica, pero fue necesario aquí para manejar el requisito de mutación).

Esto resulta ser casi tan conciso como la versión lambda, pero puede usar las firmas de métodos que desee, tienen nombres de parámetros reales, y los métodos se llaman directamente por sus nombres, sin necesidad de .apply() o cualquier cosa. (Este tipo de cosas a veces hace que las herramientas IDE funcionen un poco mejor también).

Odio usar la palabra prohibida pero podrías usar una statement goto para crear una subrutina efectiva dentro del método. Es feo y peligroso, pero es mucho más fácil que lo que se mostró en las respuestas anteriores. Aunque el método privado con una llamada dentro del primer método es mucho mejor, y le sirve muy bien. No sé por qué querrías usar un método nested para algo tan simple como esto.