Clases internas anónimas y ejemplos - 1

"¡Hola, amigo!"

"¡Pero ya te hemos dicho hola, Ellie!"

"Oye, no discutas con tu tía. En el siglo 31, si no has visto a alguien durante más de media hora, es costumbre saludar de nuevo. ¡Así que no me vengas con tu actitud!"

"De todos modos, es hora de otro tema interesante: ¡reproducción de robots!"

"O_O".

"Es broma, el nuevo tema son las clases internas anónimas ".

"En Java, a veces hay situaciones en las que necesitará una clase para heredar varias clases. Dado que Java no admite la herencia múltiple, han resuelto este problema utilizando clases internas: en nuestra clase, declaramos una clase interna y hacemos hereda cualquier clase que necesitemos que herede. Aquí hay un ejemplo:"

Ejemplo de una clase interna que hereda la clase Thread
class Tiger extends Cat
{
 public void tigerRun()
 {
  .....
 }

 public void startTiger()
 {
  TigerThread thread = new TigerThread();
  thread.start();
 }

 class TigerThread extends Thread
 {
  public void run()
  {
   tigerRun();
  }
 }
}

"Vamos a profundizar en otro ejemplo:"

Necesitamos una subclase de la clase Thread para anular su método de ejecución".

"Es por eso que en la clase Tiger declaramos la clase interna TigerThread , que hereda Thread y anula el método de ejecución.

"Para mayor comodidad, definimos dos métodos en la clase Tiger: tigerRun y ​​startTiger (que son análogos a los métodos de ejecución e inicio de Thread).

"En el método tigerStart, creamos un objeto TigerThread e invocamos su método start()".

"La JVM creará un nuevo subproceso que comenzará a ejecutarse cuando se llame al método de ejecución de TigerThread ".

"Este método luego llama a nuestro método de ejecución : tigerRun ".

"He trabajado con hilos antes, así que esto parece sencillo".

"¿Tenemos que nombrar los métodos tigerRun y ​​tigerStart?"

"No, podríamos haberlos llamado ejecutar y comenzar, pero también quería demostrar que no heredamos Thread. Una explicación podría haber sido más confusa".

"Está bien. Entonces creo que lo entiendo. Pero si se llama a tigerStart por segunda vez, crearemos e iniciaremos un segundo objeto Thread. ¿No significa eso que tendremos «un tigre ejecutándose en dos hilos diferentes»? "

"¡Bueno, no eres listo! Tienes razón, y eso no es bueno. Reescribamos el código así:"

Código
class Tiger extends Cat
{
 public void tigerRun()
 {
  .....
 }

 public void startTiger()
 {
  thread.start();
 }

 private TigerThread thread = new TigerThread();

 private class TigerThread extends Thread
 {
  public void run()
  {
   tigerRun();
  }
 }
}

"No es del todo perfecto. Todavía no puedes llamar a un método como ese dos veces. Pero esta vez, al menos no crearemos un segundo hilo y haremos que parezca que todo está bien".

"Así es. La segunda vez que se inicia un Tiger, obtendrá una excepción".

"¡Ya estoy detectando errores mejor que tú, Ellie!"

"Sí, lo estás haciendo muy bien. Entonces pasemos a las clases internas anónimas".

"Tenga en cuenta varios aspectos del código anterior:"

1) Heredamos la clase Thread, pero implementamos prácticamente ningún código allí. "Era más "teníamos que heredar la clase Thread" en lugar de "la heredamos para extenderla".

2) Solo se creará un objeto TigerThread.

En otras palabras, escribimos un montón de código solo para anular un método y crear un objeto.

¿Recuerdas cómo hablé sobre la invención de los constructores?

Antes de los constructores Después de los constructores
TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}
Thread thread = new Thread()
{
 public void run()
 {
  tigerRun();
 }
};

"Veo que el código se volvió más compacto, pero no entiendo muy bien qué está pasando".

"Podemos combinar cuatro cosas en una:"

1) declaración de una clase derivada

2) anulación del método

3) declaración de una variable

4) creación de una instancia de una clase derivada.

"De hecho, lo que estamos haciendo es combinar dos operaciones: declarar una clase derivada y crear una instancia de esa clase".

Sin clase anónima Con clase anónima
Cat tiger = new Tiger();

class Tiger extends Cat
{
}
Cat tiger = new Cat()
{
};

"Vamos a explorar la sintaxis de nuevo:"

Declaración de una variable Thread
Thread thread = new Thread();
Declaración de una variable cuyo tipo es «una clase anónima que hereda Thread»
Thread thread = new Thread()
{

};

"Tenga en cuenta que no estamos simplemente definiendo una nueva clase. Estamos creando una variable, ¡hay un punto y coma al final!"

"Y si queremos anular el método de ejecución, debemos escribir esto:"

Declaración de una variable Thread
Thread thread = new Thread()
{
 public void run()
  {
   System.out.println("new run-method");
  }
};

"Te das cuenta rápidamente. ¡Bien hecho!"

"Gracias. ¿Qué pasa si necesitamos otros métodos que no son parte de la clase Thread?"

"Puedes escribirlos".

"Aunque es anónimo, esta es una clase interna completa:"

codigo Java Descripción
Thread thread = new Thread()
{
  public void run()
  {
   printHi();
  }

  public void printHi()
  {
   System.out.println("Hi!");
  }
};
Rojo: código para crear la variable.

Verde: código para crear el objeto.

Azul: código de la clase derivada anónima.

"¿Una clase interna de pleno derecho?"

"¿Entonces puedo usar las variables de la clase externa?"

"Absolutamente."

"¿Y puedo pasarle algo al constructor?"

"Sí, pero solo los argumentos para el constructor de la superclase:"

Clase Instancia de una clase interna anónima
class Cat
{
 int x, y;
 Cat(int x, int y)
 {
  this.x = x;
  thix.y = y;
 }
}
Cat cat = new Cat(3,4)
{
  public void print()
  {
   System.out.println(x+" "+y);
  }
};

"No podemos agregar nuestros propios parámetros al constructor de otra persona. Pero podemos usar las variables de la clase externa, lo que compensa muy bien esta deficiencia".

"¿Qué pasa si todavía necesito agregar otros parámetros al constructor?"

"Luego declara una clase interna ordinaria (no anónima) y úsala".

"Claro, casi me olvido de eso."

"¿Qué sucede si declaro una variable estática? ¿Eso haría que la clase anónima se convirtiera en una clase anidada estática en lugar de una clase interna? En otras palabras, ¿le faltará una referencia a la clase externa?"

"No. Sería una clase interna anónima. Mira estos ejemplos".

Con clase anónima Sin clase anónima
Thread thread = new Thread()
{
  public void run()
  {
   tigerRun();
  }
};
TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}
static Thread thread = new Thread()
{
  public void run()
  {
   tigerRun();
  }
};
static TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}

"Ya veo. Solo la variable estática sería estática, no la clase".

"Sí."

"De hecho, el compilador crea clases internas para todas las clases internas anónimas. Estas clases generalmente se denominan «1», «2», «3», etc.