Compila los listados 5 y 6 de la siguiente manera: javac *.java Cuando compilas una clase cuyo método contiene una clase local, el compilador crea un archivo de clase para la clase local cuyo nombre consta del nombre de la clase que la contiene, un carácter de signo de dólar, un entero basado en 1 y el nombre de la clase local. En este caso, la compilación da como resultado EnclosingClass$1LClass.class y EnclosingClass.class. Una nota sobre el nombre del archivo de clase local Al generar un nombre para el archivo de clase de una clase local, el compilador agrega un entero al nombre generado. Este entero probablemente se genera para distinguir el archivo de clase de una clase local del archivo de clase de una clase miembro no estática. Si dos clases locales tienen el mismo nombre, el compilador incrementa el entero para evitar conflictos. Considere el siguiente ejemplo: public class EnclosingClass { public void m1() { class LClass { } } public void m2() { class LClass { } } public void m3() { class LClass2 { } } } EnclosingClass declara tres métodos de instancia, cada uno de los cuales declara una clase local. Los primeros dos métodos generan dos clases locales diferentes con el mismo nombre. El compilador genera los siguientes archivos de clase: EnclosingClass$1LClass.class EnclosingClass$1LClass2.class EnclosingClass$2LClass.class EnclosingClass.class Ejecute la aplicación de la siguiente manera: java LCDemo Debería observar la siguiente salida: 5 15 Ejemplo: Uso de clases locales en expresiones regulares La biblioteca de clases estándar incluye ejemplos de uso de clases locales. Por ejemplo, la clase Matcher, en java.util.regex, proporciona un método results() que devuelve un flujo de resultados de coincidencia. Este método declara una clase MatchResultIterator para iterar sobre estos resultados: public Stream resultados() { clase MatchResultIterator implementa Iterator
{ // miembros } return StreamSupport. stream(Spliterators. spliteratorUnknownSize(new MatchResultIterator(), Spliterator. ORDERED | Spliterator. NONNULL), false); } Tenga en cuenta la instanciación de MatchResultIterator() después de la declaración de clase. No se preocupe por las partes del código que no comprende; en su lugar, piense en la utilidad de poder declarar clases en los ámbitos adecuados (como el cuerpo de un método) para organizar mejor su código. Tipo de clase interna 3: Clases anónimas Las clases miembro estáticas, las clases miembro no estáticas y las clases locales tienen nombres. Por el contrario, las clases anónimas son clases anidadas sin nombre. Las introduce en el contexto de expresiones que involucran al operador new y el nombre de una clase base o una interfaz implementada por la clase anónima: // subclase de la clase base abstract class Base { // miembros } class A { void m() { Base b = new Base() { // miembros }; } } // implementar la interfaz interface I { // miembros } class B { void m() { I i = new I() { // miembros }; } } El primer ejemplo demuestra una clase anónima que extiende una clase base. La expresión new Base() va seguida de un par de llaves que indican la clase anónima. El segundo ejemplo demuestra una clase anónima que implementa una interfaz. La expresión new I() va seguida de un par de llaves que indican la clase anónima. Construcción de instancias de clase anónima Una clase anónima no puede tener un constructor explícito porque un constructor debe tener el nombre de la clase y las clases anónimas no tienen nombre. En su lugar, puede utilizar un bloque de inicialización de objeto ({}) como constructor. Las clases anónimas son útiles para expresar la funcionalidad que se pasa a un método como su argumento. Por ejemplo, considere un método para ordenar una matriz de números enteros. Desea ordenar la matriz en orden ascendente o descendente, según las comparaciones entre pares de elementos de la matriz. Puede duplicar el código de ordenación, con una versión que utilice el menor que (<) operator for one order, and the other version using the greater than (>) para el orden opuesto. Alternativamente, como se muestra a continuación, puede diseñar el código de ordenamiento para invocar un método de comparación y luego pasar un objeto que contenga este método como argumento al método de ordenamiento. Listado 7. Uso de una clase anónima para pasar funcionalidad como argumento de método (Comparer.java) public abstract class Comparer { public abstract int compare(int x, int y); } El método compare() se invoca con dos elementos de matriz de enteros y devuelve uno de tres valores: un valor negativo si x es menor que y, 0 si ambos valores son iguales y un valor positivo si x es mayor que y. El Listado 8 presenta una aplicación cuyo método sort() invoca compare() para realizar las comparaciones. Listado 8. Ordenamiento de una matriz de enteros con el algoritmo Bubble Sort (ACDemo.java) public class ACDemo { public static void main(String[] argumentos) { int[] a = { 10, 30, 5, 0, -2, 100, -9 }; dump(a); sort(a, new Comparer() { public int compare(int x, int y) { return x – y; } }); dump(a); int[] b = { 10, 30, 5, 0, -2, 100, -9 }; sort(b, new Comparer() { public int compare(int x, int y) { return y – x; } }); dump(b); } static void dump(int[] x) { para (int i = 0; i < x.length; i++) System.out.print(x[i] + " "); System.out.println(); } void estático ordenar(int[] x, Comparador c) { para (int pass = 0; pass < x.length - 1; pass++) for (int i = x.length - 1; i > pasar; i–) si (c.comparar(x[i]incógnita[pass]) < 0) { int temperatura = x[i];x[i] = x[pass];x[pass] = temp; } } } El método main() revela dos llamadas a su método complementario sort(), que ordena una matriz de números enteros a través del algoritmo Bubble Sort. Cada llamada recibe una matriz de números enteros como su primer argumento y una referencia a un objeto creado a partir de una subclase anónima de Comparer como su segundo argumento. La primera llamada logra una ordenación en orden ascendente restando y de x; la segunda llamada logra una ordenación en orden descendente restando x de y. Migración de clases anónimas a lambdas Como puede ver en el Listado 8, pasar funcionalidad anónima basada en clases puede llevar a una sintaxis verbosa. A partir de Java 8, puede usar lambdas para un código más conciso. Consulte Introducción a las expresiones lambda en Java para obtener más información sobre el uso de lambdas en su código Java. Compila los listados 7 y 8 de la siguiente manera: javac *.java Cuando compilas una clase cuyo método contiene una clase anónima, el compilador crea un archivo de clase para la clase anónima cuyo nombre consta del nombre de la clase que la encierra, un carácter de signo de dólar y un entero que identifica de forma única a la clase anónima. En este caso, la compilación da como resultado ACDemo$1.class y ACDemo$2.class además de ACDemo.class. Ejecuta la aplicación de la siguiente manera: java ACDemo Deberías observar la siguiente salida: 10 30 5 0 -2 100 -9 -9 -2 0 5 10 30 100 100 30 10 5 0 -2 -9 Ejemplo: Uso de clases anónimas con un controlador de eventos AWT Las clases anónimas se pueden usar con muchos paquetes en la biblioteca de clases estándar. Para este ejemplo, usaremos una clase anónima como controlador de eventos en Abstract Windowing Toolkit o Swing Windowing Toolkit. El siguiente fragmento de código registra un controlador de eventos con la clase JButton de Swing, que se encuentra en el paquete javax.swing. JButton describe un botón que realiza una acción (en este caso, imprimir un mensaje) cuando se hace clic en él. JButton btnClose = new JButton("close"); btnClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { System.out.println("close button clicked"); } }); La primera línea crea una instancia de JButton y pasa close como etiqueta del botón al constructor de JButton. La segunda línea registra un detector de acciones con el botón. El método actionPerformed() del detector de acciones se invoca siempre que se hace clic en el botón. El objeto que se pasa a addActionListener() se crea una instancia desde una clase anónima que implementa la interfaz java.awt.event.ActionListener. Interfaces y clases anidadas Java también permite anidar interfaces en interfaces y clases en interfaces. Consulte este artículo sobre interfaces anidadas para obtener más información sobre esta característica extraña pero ocasionalmente útil del lenguaje Java. Conclusión Las capacidades de anidación de Java lo ayudan a organizar tipos de referencia que no son de nivel superior. Para los tipos de referencia de nivel superior, Java proporciona paquetes. El siguiente tutorial de Java 101 le presenta los paquetes y las importaciones estáticas en Java. Más de este autor