CodeGym/Blog Java/Random-ES/Clases internas anidadas
Autor
John Selawsky
Senior Java Developer and Tutor at LearningTree

Clases internas anidadas

Publicado en el grupo Random-ES
¡Hola! Hoy abordaremos un tema importante: cómo funcionan las clases anidadas en Java. Java te permite crear clases dentro de otra clase:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Estas clases internas se denominan anidadas. Se dividen en 2 tipos:
  1. Clases anidadas no estáticas. También se denominan clases internas.
  2. Clases estáticas anidadas.
A su vez, las clases internas tienen dos subcategorías distintas. Además de ser una clase interna simplemente una clase interna, también puede ser:
  • una clase local
  • una clase anónima
¿Confundido? :) Esta bien. Aquí hay un diagrama para mayor claridad. ¡Regresa a él durante la lección si de repente te encuentras confundido! Clases internas anidadas - 2En la lección de hoy, hablaremos de las clases internas (también conocidas como clases anidadas no estáticas). Están especialmente resaltados en el diagrama general para que no se pierda :) Comencemos con la pregunta obvia: ¿por qué se llaman clases "internas"? La respuesta es bastante simple: porque se crean dentro de otras clases. Aquí hay 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("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Aquí tenemos la Bicycleclase. Tiene 2 campos y 1 método: start(). Clases internas anidadas - 3Se diferencia de una clase ordinaria en que contiene dos clases: Handlebary Seat. Su código está escrito dentro de la Bicycleclase. Estas son clases completas: como puede ver, cada una de ellas tiene sus propios métodos. En este punto, es posible que tenga una pregunta: ¿por qué demonios pondríamos una clase dentro de otra? ¿Por qué hacerlos clases internas? Bueno, supongamos que necesitamos clases separadas para los conceptos de manillar y asiento en nuestro programa. ¡Por supuesto, no es necesario que los hagamos anidados! Podemos hacer clases ordinarias. Por ejemplo, así:
public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
¡Muy buena pregunta! Por supuesto, no estamos limitados por la tecnología. Hacer eso es ciertamente una opción. Aquí, lo importante es más el diseño correcto de las clases desde la perspectiva de un programa específico y su propósito. Las clases internas son para separar una entidad que está indisolublemente conectada a otra entidad. Los manillares, los asientos y los pedales son componentes de una bicicleta. Separados de la bicicleta, no tienen mucho sentido. Si hiciéramos todos estos conceptos como clases públicas separadas, tendríamos un código como este en nuestro programa:
public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Hmm... El significado de este código es incluso difícil de explicar. Tenemos un manillar vago (¿Por qué es necesario? Ni idea, para ser honesto). Y este mango gira a la derecha... por sí solo, sin bicicleta... por alguna razón. Al separar el concepto de manillar del concepto de bicicleta, perdimos algo de lógica en nuestro programa. Usando una clase interna, el código se ve muy 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 la consola:
Seat up!
Let's go!
Steer left!
Steer right!
¡Ahora lo que vemos de repente tiene sentido! :) Creamos un objeto de bicicleta. Creamos dos "subobjetos" de bicicleta: un manillar y un asiento. Levantamos el asiento para mayor comodidad y nos fuimos: pedaleando y dirigiendo según sea necesario. :) Los métodos que necesitamos se llaman en los objetos apropiados. Todo es simple y conveniente. En este ejemplo, separar el manillar y el asiento mejora la encapsulación (ocultamos datos sobre las piezas de la bicicleta dentro de la clase correspondiente) y nos permite crear una abstracción más detallada. Ahora veamos una situación diferente. Supongamos que queremos crear un programa que simule una tienda de bicicletas y repuestos para bicicletas. Clases internas anidadas - 4En esta situación, nuestra solución anterior no funcionará. En una tienda de bicicletas, cada pieza individual de la bicicleta tiene sentido incluso cuando se separa de una bicicleta. Por ejemplo, necesitaremos métodos como "vender pedales a un cliente", "comprar un asiento nuevo", etc. Sería un error usar clases internas aquí: cada parte individual de la bicicleta en nuestro nuevo programa tiene un significado que se sostiene en propio: se puede separar del concepto de bicicleta. Esto es precisamente a lo que debe prestar atención si se pregunta si debe usar clases internas u organizar todas las entidades como clases separadas. La programación orientada a objetos es buena porque facilita el modelado de entidades del mundo real. Este puede ser su principio rector al decidir si usar clases internas. En una tienda real, las piezas de repuesto están separadas de las bicicletas, esto está bien. Esto significa que también está bien al diseñar un programa. Bien, hemos descubierto la "filosofía" :) Ahora vamos a familiarizarnos con importantes características "técnicas" 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 tiene sentido: es por eso que creamos las clases internas Seaty Handlebaren nuestro programa, para que no terminemos con manubrios y asientos huérfanos.

    Este código no compila:

    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }

    Otra característica importante se desprende de esto:

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

    Por ejemplo, agreguemos una int seatPostDiametervariable (que representa el diámetro de la tija del sillín) a nuestra Bicycleclase.

    Luego, en la Seatclase interna, podemos crear un displaySeatProperties()método que muestre las propiedades 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("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    Y ahora podemos mostrar 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.displaySeatProperties();
       }
    }

    Salida de la consola:

    Seat properties: seatpost diameter = 40

    Nota:la nueva variable se declara con el modificador de acceso más estricto ( private). ¡Y todavía la clase interna tiene acceso!

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

    Esto se explica por las características específicas de cómo se organizan las clases internas. Una clase interna puede tener constructores con parámetros, o simplemente el constructor predeterminado. Pero independientemente, cuando creamos un objeto de una clase interna, una referencia al objeto de la clase externa se pasa de manera invisible al objeto creado de la clase interna. Después de todo, la presencia de tal referencia de objeto es un requisito absoluto. De lo contrario, no podremos crear objetos de la clase interna.

    Pero si un método de la clase externa es estático, ¡es posible que no tengamos un objeto de la clase externa! Y esto sería una violación de la lógica de cómo funciona una clase interna. En esta situación, el compilador generará 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 es la misma: los métodos estáticos y las variables pueden existir y ser llamados o referenciados incluso en ausencia de un objeto.

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

    ¡Una clara contradicción! Esta es la razón por la cual las variables y los métodos estáticos no están permitidos en las clases internas.

    El compilador generará un error si intenta crearlos:

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. Al crear un objeto de una clase interna, su modificador de acceso es importante.

    Una clase interna se puede marcar con los modificadores de acceso estándar: public, private, protectedy package private.

    ¿Por qué importa esto?

    Esto afecta dónde podemos crear instancias de la clase interna en nuestro programa.

    Si nuestra Seatclase se declara como public, podemos crear Seatobjetos en cualquier otra clase. El único requisito es que también debe existir un objeto de la clase externa.

    Por cierto, esto ya lo hicimos 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();
       }
    }

    Obtuvimos acceso fácilmente a la Handlebarclase interna desde la Mainclase.

    Si declaramos la clase interna como private, podremos crear objetos solo dentro de la clase externa.

    Ya no podemos crear un Seatobjeto "en el exterior":

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

    Probablemente ya entiendas la lógica :)

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

    El protectedmodificador proporciona acceso a una variable de instancia en subclases y clases que están en el mismo paquete.

    protectedtambién funciona para clases internas. Podemos crear protectedobjetos de la clase interna:

    • en la clase exterior;
    • en sus subclases;
    • en 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:

    • en la clase exterior;
    • en clases que están en el mismo paquete.

    Ha estado familiarizado con los modificadores durante mucho tiempo, así que no hay problemas aquí.

Eso es todo por ahora :) ¡Pero no te desanimes! Las clases internas son un tema bastante extenso que continuaremos explorando en la próxima lección. Ahora puede refrescar su memoria de la lección de nuestro curso sobre clases internas . Y la próxima vez, hablemos de clases anidadas estáticas.
Comentarios
  • Populares
  • Nuevas
  • Antiguas
Debes iniciar sesión para dejar un comentario
Esta página aún no tiene comentarios