JavaRush /Blog Java /Random-ES /Clases internas anidadas o Inner Class en Java

Clases internas anidadas o Inner Class en Java

Publicado en el grupo Random-ES
¡Hola! Hoy comenzaremos a analizar un tema importante: cómo funcionan las clases anidadas en Java. En inglés se llaman clases anidadas. Java te permite crear algunas clases dentro de otras:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Son estas clases las que se llaman anidadas. Se dividen en 2 tipos:
  1. Clases anidadas no estáticas: clases anidadas no estáticas. También se les llama clases internas de otra manera.
  2. Clases anidadas estáticas: clases anidadas estáticas.
A su vez, las clases internas tienen dos subtipos especiales. Además del hecho de que una clase interna puede ser simplemente una clase interna, también puede ser:
  • clase local
  • clase anónima
¿Un poco difícil? :) Está bien, aquí hay un diagrama para mayor claridad. ¡Vuelva a consultarlo durante la conferencia si de repente se siente confundido! Clases internas anidadas - 2En la conferencia de hoy hablaremos sobre clases internas: clases internas (también son clases anidadas no estáticas, clases anidadas no estáticas). Están especialmente resaltadas en el diagrama general para que no te pierdas :) Comencemos con la pregunta obvia: ¿por qué estas clases se llaman “internas”? La respuesta es bastante sencilla: porque se crean dentro de otras clases. He aquí un ejemplo:
public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }

   public void start() {
       System.out.println("¡Ir!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("¡Volante a la derecha!");
       }

       public void left() {

           System.out.println("¡Volante a la izquierda!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("¡El asiento está arriba!");
       }

       public void down() {

           System.out.println("¡El asiento está abajo!");
       }
   }
}
Aquí tenemos una clase Bicycle: bicicleta. Tiene 2 campos y 1 método - start(). Clases internas anidadas - 3Su diferencia con una clase normal es que tiene dos clases, cuyo código está escrito en su interior Bicycle: estas son las clases HandleBar(volante) y Seat(asiento). Estas son clases completas: como puede ver, cada una de ellas tiene sus propios métodos. Llegados a este punto, es posible que tengas una pregunta: ¿por qué pusimos una clase dentro de otra? ¿Por qué hacerlos internos? Bien, digamos que necesitamos clases separadas para el volante y el asiento en el programa. ¡Pero no es necesario que los anides! Puedes realizar clases regulares. Por ejemplo, así:
public class HandleBar {
   public void right() {
       System.out.println("¡Volante a la derecha!");
   }

   public void left() {

       System.out.println("Volante a la izquierda");
   }
}

public class Seat {

   public void up() {

       System.out.println("¡El asiento está arriba!");
   }

   public void down() {

       System.out.println("¡El asiento está abajo!");
   }
}
¡Muy buena pregunta! Por supuesto, no tenemos limitaciones técnicas: podemos hacerlo de esta manera. Se trata más de diseñar clases correctamente desde el punto de vista de un programa en particular y en el significado de ese programa. Las clases internas son clases para resaltar una determinada entidad en un programa que está indisolublemente ligada a otra entidad. El volante, el asiento y los pedales son los componentes de una bicicleta. Separados de la bicicleta, no tienen sentido. Si hiciéramos que todas estas clases fueran clases públicas separadas, nuestro programa podría tener, por ejemplo, el siguiente código:
public class Main {

   public static void main(String[] args) {
       HandleBar handleBar = new HandleBar();
       handleBar.right();
   }
}
Ummm... El significado de este código es incluso difícil de explicar. Tenemos una especie de manillar de bicicleta extraño (¿por qué es necesario? Ni idea, para ser honesto). Y este volante gira hacia la derecha... solo, sin bicicleta... por alguna razón. Al separar la esencia del volante de la esencia de la bicicleta, hemos perdido la lógica de nuestro programa. Usando una clase interna, el código se ve completamente diferente:
public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.HandleBar handleBar = peugeot.new HandleBar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handleBar.left();
       handleBar.right();
   }
}
Salida de consola:

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
¡Lo que estaba pasando de repente cobró sentido! :) Hemos creado un objeto bicicleta. Creamos dos de sus "subobjetos": el volante y el asiento. Levantamos el asiento más para mayor comodidad y nos pusimos manos a la obra: ¡giramos y giramos hacia donde necesitamos ir! :) Los métodos que necesitamos se llaman en los objetos necesarios. Todo es simple y conveniente. En este ejemplo, resaltar el manillar y el asiento mejora la encapsulación (estamos ocultando datos sobre las partes de la bicicleta dentro de la clase correspondiente) y nos permite crear una abstracción más detallada. Ahora veamos otra situación. Digamos que queremos crear un programa que modele una tienda de bicicletas y repuestos. Clases internas anidadas - 4En esta situación, nuestra solución anterior fallará. Dentro de los límites de una tienda de repuestos, cada pieza individual de una bicicleta tiene un significado incluso más allá de la esencia de la bicicleta. Por ejemplo, necesitaremos métodos como “vender pedales a un comprador”, “comprar un asiento nuevo”, etc. Sería un error utilizar aquí clases internas: cada parte individual de la bicicleta dentro de nuestro nuevo programa tiene su propio significado: está separada de la esencia de la bicicleta y de ninguna manera está ligada a ella. Esto es a lo que debe prestar atención si se pregunta si necesita usar clases internas o separar todas las entidades en clases separadas. La programación orientada a objetos es excelente porque facilita el modelado de entidades del mundo real. Esto es lo que puede utilizar como guía a la hora de decidir si utilizar clases internas. En una tienda real, las piezas están separadas de las bicicletas; esto es normal. Esto significa que esto será correcto al diseñar un programa. Bien, hemos resuelto la "filosofía" :) Ahora conozcamos las características "técnicas" importantes de las clases internas. Esto es lo que definitivamente necesita recordar y comprender:
  1. Un objeto de una clase interna no puede existir sin un objeto de una clase "externa".

    Esto es lógico: por eso las hicimos clases Seatinternas HandleBar, para que los volantes y asientos sin dueño no aparecieran aquí y allá en nuestro programa.

    Este código no se compilará:

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    De esto se desprende la siguiente característica importante:

  2. Un objeto de una clase interna tiene acceso a las variables de la clase "externa".

    Por ejemplo, agreguemos Bicycleuna variable a nuestra clase int seatPostDiameter: el diámetro de la tija del sillín.

    Luego en la clase interna Seatpodemos crear un método getSeatParam()que nos dirá el parámetro del asiento:

    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("¡Ir!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("¡El asiento está arriba!");
           }
    
           public void down() {
    
               System.out.println("¡El asiento está abajo!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Parámetro del asiento: diámetro de la tija = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    Y ahora podemos obtener esta información en nuestro programa:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.getSeatParam();
       }
    }

    Salida de consola:

    
    Параметр сиденья: диаметр подседельного штыря = 40

    Prestar atención:la nueva variable se declara con el modificador más estricto - private. ¡Y aún así la clase interna tiene acceso!

  3. No se puede crear un objeto de clase interna en un método estático de una clase "externa".

    Esto se explica por las características de diseño de las clases internas. Una clase interna puede tener constructores con parámetros o simplemente un constructor predeterminado. Pero independientemente de esto, cuando creamos un objeto de la clase interna, se le pasa silenciosamente una referencia a un objeto de la clase "externa". Después de todo, la presencia de tal objeto es un requisito previo. De lo contrario no podremos crear objetos de la clase interna.

    Pero si el método de la clase externa es estático, ¡es posible que el objeto de la clase externa no exista en absoluto! Esto significa que se romperá la lógica de la clase interna. En tal situación, el compilador arrojará un error:

    public static Seat createSeat() {
    
       //Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
  4. Una clase interna no puede contener variables y métodos estáticos.

    La lógica aquí es la misma: los métodos y variables estáticos pueden existir y llamarse incluso si no hay ningún objeto.

    Pero sin un objeto de la clase "externa", no tendremos acceso a la clase interna.

    ¡Una contradicción obvia! Por tanto, está prohibida la presencia de variables y métodos estáticos en clases internas.

    El compilador arrojará un error al intentar crearlos:

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           //inner class cannot have static declarations
           public static void getSeatParam() {
    
               System.out.println("Parámetro del asiento: diámetro de la tija = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. Al crear un objeto de clase interna, su modificador de acceso juega un papel importante.

    Una clase interna puede indicarse mediante los modificadores de acceso estándar : public, y .privateprotectedpackage private

    ¿Por qué es importante?

    Esto afecta en qué parte de nuestro programa podemos crear una instancia de la clase interna.

    Si nuestra clase Seatse declara como public, podemos crear sus objetos en cualquier otra clase. El único requisito es que el objeto de la clase "externa" también debe existir.

    Por cierto, esto ya lo hemos hecho aquí:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.HandleBar handleBar = peugeot.new HandleBar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handleBar.left();
           handleBar.right();
       }
    }

    Accedimos fácilmente a la clase interna HandleBardesde Main.

    Si declaramos la clase interna como private, solo tendremos acceso a crear objetos dentro de la clase "externa".

    SeatYa no podremos crear un objeto desde fuera:

    private class Seat {
    
       //métodos
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           //Bicycle.Seat has a private access in 'Bicycle'
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    Probablemente ya entiendas la lógica :)

  6. Los modificadores de acceso para clases internas funcionan igual que para las variables regulares.

    El modificador protectedproporciona acceso a una variable de clase en sus clases descendientes y en clases que están en el mismo paquete.

    Lo mismo protectedfunciona para las clases internas. Se pueden crear objetos protectedde clase interna :

    • dentro de la clase "externa";
    • en sus clases descendientes;
    • en aquellas clases que están en el mismo paquete.

    Si la clase interna no tiene un modificador de acceso ( package private), se pueden crear objetos de la clase interna

    • dentro de la clase "externa";
    • en clases que están en el mismo paquete.

    Usted conoce los modificadores desde hace mucho tiempo, por lo que no habrá ningún problema aquí.

Eso es todo por ahora :) ¡Pero no te relajes! Las clases anidadas internas son un tema bastante amplio que continuaremos explorando en lecciones futuras. Ahora puedes repasar la charla sobre clases internas de nuestro curso. Y la próxima vez hablaremos de clases anidadas estáticas.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION