CodeGym/Blog Java/Random-ES/Ejemplos de herencia de clases anidadas
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

Ejemplos de herencia de clases anidadas

Publicado en el grupo Random-ES
¡Hola! Hoy veremos un mecanismo importante: la herencia en clases anidadas. ¿Alguna vez ha pensado en lo que haría si necesitara hacer que una clase anidada herede otra clase? Si no, créame: esta situación puede ser confusa, porque hay muchos matices.
  1. ¿Estamos haciendo que una clase anidada herede alguna clase? ¿O estamos haciendo que alguna clase herede una clase anidada?
  2. ¿La clase hijo/padre es una clase pública normal o también es una clase anidada?
  3. Finalmente, ¿qué tipo de clases anidadas usamos en todas estas situaciones?
Hay tantas respuestas posibles a todas estas preguntas que tu cabeza dará vueltas :) Como sabes, podemos resolver un problema complejo dividiéndolo en partes más simples. Vamos a hacer eso. Consideremos cada grupo de clases anidadas a su vez desde dos perspectivas: quién puede heredar cada tipo de clase anidada y quién puede heredar. Comencemos con las clases anidadas estáticas.

Clases estáticas anidadas

Ejemplos de herencia de clases anidadas - 2Sus reglas de herencia son las más simples. Aquí puedes hacer casi cualquier cosa que tu corazón desee. Una clase anidada estática puede heredar:
  • una clase ordinaria
  • una clase anidada estática que se declara en una clase externa o sus ancestros
Recuerde un ejemplo de nuestra lección sobre clases anidadas estáticas.
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Intentemos cambiar el código y crear una Drawingclase anidada estática y su descendiente: Boeing737Drawing.
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Como puedes ver, no hay problema. Incluso podemos extraer la Drawingclase y convertirla en una clase pública ordinaria en lugar de una clase anidada estática: nada cambiará.
public class Drawing {

}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Entendemos esto. Pero, ¿qué clases pueden heredar una clase anidada estática? ¡Prácticamente cualquiera! Anidado/no anidado, estático/no estático: no importa. Aquí hacemos que la Boeing737Drawingclase interna herede la Drawingclase anidada estática:
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Puedes crear una instancia de Boeing737Drawingcomo esta:
public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
Aunque nuestra Boeing737Drawingclase hereda una clase estática, ¡no es estática en sí misma! Como resultado, siempre necesitará una instancia de la clase externa. Podemos eliminar la Boeing737Drawingclase de la Boeing737clase y convertirla en una clase pública simple. Nada cambia. Todavía puede heredar la Drawingclase anidada estática.
public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;

}
El único punto importante es que en este caso necesitamos hacer maxPassengersCountpública la variable estática. Si sigue siendo privado, una clase pública ordinaria no tendrá acceso a él. ¡Hemos descubierto las clases estáticas! :) Ahora pasemos a las clases internas. Vienen en 3 tipos: clases internas simples, clases locales y clases internas anónimas. Ejemplos de herencia de clases anidadas - 3Nuevamente, pasemos de lo simple a lo complejo :)

Clases internas anónimas

Una clase interna anónima no puede heredar otra clase. Ninguna otra clase puede heredar una clase anónima. ¡No podría ser más sencillo! :)

clases locales

Las clases locales (en caso de que lo hayas olvidado) se declaran dentro de un bloque de código de otra clase. La mayoría de las veces, esto sucede dentro de algún método de la clase externa. Lógicamente, solo otras clases locales dentro del mismo método (o bloque de código) pueden heredar una clase local. Aquí hay un ejemplo:
public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {


       }

       // ...number validation code
   }
}
Este es el código de nuestra lección sobre clases locales. Nuestra clase de validador de números tiene una PhoneNumberclase local. Si necesitamos que represente dos entidades distintas, por ejemplo, un número de teléfono móvil y un número de teléfono fijo, solo podemos hacerlo dentro del mismo método. La razón es simple: el alcance de una clase local está limitado al método (bloque de código) donde se declara. Como resultado, no podremos usarlo externamente (incluso para la herencia de clases). Sin embargo, las posibilidades de herencia dentro de la propia clase local son mucho más amplias. Una clase local puede heredar:
  1. Una clase ordinaria.
  2. Una clase interna que se declara en la misma clase que la clase local o en uno de sus ancestros.
  3. Otra clase local declarada en el mismo método (bloque de código).
El primer y tercer punto parecen obvios, pero el segundo es un poco confuso :/ Veamos dos ejemplos. Ejemplo 1: "Hacer que una clase local herede una clase interna declarada en la misma clase que la clase local":
public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
Aquí eliminamos la PhoneNumberclase del validatePhoneNumber()método y la convertimos en una clase interna en lugar de una clase local. Esto no nos impide hacer que nuestras 2 clases locales lo hereden. Ejemplo 2: "... o en los antepasados ​​de esta clase". Ahora esto ya es más interesante. Podemos avanzar PhoneNumberaún más en la cadena de herencia. Declaremos una AbstractPhoneNumberValidatorclase abstracta, que se convertirá en el ancestro de nuestra PhoneNumberValidatorclase:
public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
Como puede ver, no solo lo declaramos, sino que también trasladamos la PhoneNumberclase interna a él. Sin embargo, en su descendiente PhoneNumberValidator, las clases locales declaradas en los métodos pueden heredar PhoneNumbersin ningún problema.
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
Debido a la relación de herencia, las clases locales dentro de una clase descendiente "ven" las clases internas dentro de un ancestro. Y finalmente, pasemos al último grupo :)

clases internas

Una clase interna declarada en la misma clase externa (o en su descendiente) puede heredar otra clase interna. Exploremos esto usando nuestro ejemplo con bicicletas de la lección sobre clases internas.
public class Bicycle {

   private String model;
   private int maxWeight;

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

   public void start() {
       System.out.println("Let's go!");
   }

   class Seat {

       public void up() {

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

       public void down() {

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

   class SportSeat extends Seat {

       // ...methods
   }
}
Aquí declaramos la Seatclase interna dentro de la Bicycleclase. Un tipo especial de asiento de carreras, SportSeatlo hereda. Pero, podríamos crear un tipo de "bicicleta de carreras" separado y ponerlo en una clase separada:
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int maxWeight) {
       super(model, maxWeight);
   }


   class SportSeat extends Seat {

       public void up() {

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

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Esta también es una opción. La clase interna del descendiente ( SportBicycle.SportSeat) "ve" las clases internas del antepasado y puede heredarlas. ¡Heredar clases internas tiene una característica muy importante! En los dos ejemplos anteriores, nuestra SportSeatclase era una clase interna. Pero, ¿y si decidimos hacer SportSeatuna clase pública ordinaria que herede simultáneamente la Seatclase interna?
// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

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

   public void down() {

       System.out.println("Seat down!");
   }
}
¡Tenemos un error! ¿Puedes adivinar por qué? :) Todo es sencillo. Cuando hablamos de la Bicycle.Seatclase interna, mencionamos que una referencia a una instancia de la clase externa se pasa implícitamente al constructor de la clase interna. Esto significa que no puede crear un Seatobjeto sin crear un Bicycleobjeto. Pero, ¿qué pasa con la creación de un SportSeat? A diferencia de Seat, no tiene este mecanismo incorporado para pasar implícitamente al constructor una referencia a una instancia de la clase externa. Aun así, sin un Bicycleobjeto, no podemos crear un SportSeatobjeto, como en el caso de Seat. Por lo tanto, solo nos queda una cosa por hacer: pasar explícitamente al SportSeatconstructor una referencia a un Bicycleobjeto. Aquí está cómo hacerlo:
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

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

   public void down() {

       System.out.println("Seat down!");
   }
}
Llamamos al constructor de la superclase usando super(); Now, si queremos crear un SportSeatobjeto, nada nos impedirá hacerlo:
public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
¡Uf! Esta lección fue bastante larga :) ¡Pero aprendiste mucho! ¡Ahora es el momento de resolver algunas tareas! :)
Comentarios
  • Populares
  • Nuevas
  • Antiguas
Debes iniciar sesión para dejar un comentario
Esta página aún no tiene comentarios