Amigos, como ya notaron, a fines de junio, comenzaremos muchos grupos nuevos, entre ellos la próxima transmisión del curso
"Desarrollador Java" , que a todos les gustó. En este momento estamos compartiendo con ustedes una nueva traducción preparada para los estudiantes de este curso.

Al agregar nuevas características al lenguaje de programación, los desarrolladores de lenguaje generalmente prestan atención a los conflictos con las características actuales del lenguaje, a los cambios que conducen a la incompatibilidad con versiones anteriores, a los errores y a cualquier situación que pueda conducir a un comportamiento indefinido o inesperado.
Sin embargo, a menudo no prestan suficiente atención a cambios menores en técnicas nuevas y prácticas para escribir código. Estos cambios son a menudo efectos secundarios de nuevas características. Los cambios de este tipo no son, estrictamente hablando, nuevas características. Estos son cambios sutiles que han aparecido debido a otras características o sus combinaciones.
Clases internas anónimas
En Java, las clases internas son clases definidas como miembros de una clase. Hay cuatro tipos de clases internas:
- estático anidado
- interno
- local (método local)
- anónimo
Las clases internas anónimas son clases sin nombre que proporcionan una implementación de una clase existente. Dichas clases se usan ampliamente para manejar eventos en programación orientada a eventos. Por lo general, una clase interna anónima proporciona una implementación sobre la marcha de una clase abstracta. Sin embargo, esto no es necesario. Se puede crear una clase interna anónima a partir de una clase no abstracta.
Creo que hay un punto que no se entiende completamente en relación con las clases internas anónimas. El hecho es que el programador realmente
crea una subclase de la clase fuente . Esta subclase recibe el nombre de
Class$X
, donde
Class
es la clase externa y
X
es el número que representa el orden en que se crean las instancias de las clases internas en la clase externa. Por ejemplo,
AnonDemo$3
es la tercera clase interna creada en
AnonDemo
. No puede llamar a estas clases de la manera habitual. Y, a diferencia de otros tipos de clases internas, una clase interna anónima siempre es implícitamente una clase secundaria del tipo sobre la base de la cual se creó (con la excepción del uso de
var
, que veremos pronto).
Veamos un ejemplo.
class Anon { }; public class AnonDemo { public static void main (String[] args) { Anon anonInner = new Anon () { public String toString() { return "Overriden"; }; public void doSomething() { System.out.println("Blah"); }; }; System.out.println(anonInner.toString()); anonInner.doSomething();
En este ejemplo, instanciamos una clase interna anónima basada en la clase Anon. En esencia,
creamos una subclase sin nombre de una clase en particular . Antes de Java 10, las clases internas anónimas eran casi siempre implícitamente polimórficas. Digo "casi", porque ese código no polimórfico como este, por supuesto, se ejecutará.
new Anon() { public void foo() { System.out.println("Woah"); } }.foo();
Sin embargo, si queremos asignar el resultado de crear una instancia de una clase interna anónima al tipo original, entonces dicha operación será de naturaleza polimórfica. Las razones de esto radican en el hecho de que creamos implícitamente una subclase de la clase que especificamos como fuente para la clase interna anónima y no podremos acceder al tipo de objeto específico (
Class$X
) para especificarlo en el código fuente.
Polimorfismo y clases internas anónimas, consecuencias prácticas.
¿Notaste el código anterior? Como utilizamos una referencia de clase base a un objeto de subclase, de acuerdo con las leyes del polimorfismo, solo podemos referirnos a 1) métodos definidos por la clase base o 2) métodos virtuales anulados en la subclase.
Por lo tanto, en el fragmento de código anterior, llamar a
toString()
para un objeto anónimo de clase interna nos daría un valor "Overridden" anulado, sin embargo, llamar a
doSomething()
daría lugar a un error de compilación. Cual es la razon
Un objeto de subclase con una referencia al tipo de la clase base no tiene acceso a los miembros de la subclase a través de esta referencia a la clase base. La única excepción a esta regla es si una subclase anula el método de la clase base. En este caso, Java, fiel a su naturaleza polimórfica, utiliza el Despacho de método dinámico para seleccionar la versión del método de subclase virtual en tiempo de ejecución.
Si aún no lo sabía, un
método virtual es un método que se puede anular. En Java, todos los métodos no finales, no privados y no estáticos son virtuales de forma predeterminada. Hablo por defecto, y no implícitamente, porque diferentes jvm pueden realizar optimizaciones que pueden cambiar esto.
¿Qué tiene que ver Java 10 con él?
Una pequeña característica llamada inferencia de tipos. Eche un vistazo al siguiente ejemplo:
class Anon { }; public class AnonDemo { public static void main(String[] args) { var anonInner = new Anon() { public void hello() { System.out.println( "New method here, and you can easily access me in Java 10!\n" + "The class is: " + this.getClass() ); }; }; anonInner.hello();
Funciona, podemos llamar a
hello()
! El diablo está en los detalles. Si está familiarizado con
var
, entonces ya comprende lo que está sucediendo aquí. Usando el nombre reservado de tipo
var
,
Java pudo determinar el tipo exacto de la clase interna anónima. Por lo tanto, ya no estamos limitados a hacer referencia a la clase base para acceder al objeto de subclase.¿Qué hicimos antes de Java 10 cuando necesitábamos una referencia a una subclase?
No es ningún secreto que en el pasado distante, pero no olvidado, hubo un gran debate sobre las clases internas. Y si esto es un secreto, entonces este es uno de los peores secretos del mundo. Sin lugar a dudas, muchos no aprobarán el hecho de que necesita una referencia exacta a una clase interna anónima, ya que la idea es evitar agregar basura a la clase. Las clases anónimas deben usarse para ejecutar un contrato sobre la marcha, para una operación relacionada lógicamente con otra clase, por ejemplo, para manejar eventos. Sin embargo, probablemente, a la curiosa Bárbara no se le arrancó la nariz, y apuesto a que la mayoría de los desarrolladores son muy curiosos. ¡Quizás en detrimento del sentido común!
Antes de Java 10, podemos lograr un efecto similar usando la reflexión, de la siguiente manera:
Anon anonInner2 = new Anon() { public void hello() { System.out.println("Woah! "); }; }; anonInner2.getClass().getMethod("hello").invoke(anonInner2);
Fuente completa
Puede tomar
aquí public class VarAnonInner { public static void main (String[] args) throws Exception { var anonInner = new Anon() { public void hello() { System.out.println("New method here, and you can easily access me in Java 10!\n" + "The class is: " + this.getClass() ); }; }; anonInner.hello(); Anon anonInner2 = new Anon() { public void hello() { System.out.println("Woah! "); }; }; anonInner2.getClass().getMethod("hello").invoke(anonInner2); new Anon() { public void hello() { System.out.println("Woah!!! "); }; }.hello();
Estamos a la espera de sus comentarios, así como recordamos que un
seminario web gratuito sobre el curso tendrá lugar hoy a las 20.00.