JavaRush /Blog Java /Random-ES /Clases internas en método local.

Clases internas en método local.

Publicado en el grupo Random-ES
¡Hola! Hablemos de otro tipo de clase anidada. Es decir, sobre clases locales (método de clases internas locales). Lo primero que debes recordar antes de estudiar es su lugar en la estructura de las clases anidadas. Clases internas en método local - 2Según nuestro diagrama, podemos entender que las clases locales son un subtipo de clases internas, de las que hablamos en detalle en uno de los materiales anteriores . Sin embargo, las clases locales tienen una serie de características y diferencias importantes con respecto a las clases internas. La clave está en su declaración: una clase local se declara sólo en un bloque de código. La mayoría de las veces, dentro de algún método de una clase externa. Por ejemplo, podría verse así:
public class PhoneNumberValidator {

   public void validatePhoneNumber(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;
           }
       }

       //...código валидации номера
   }
}
¡IMPORTANTE!Este código no se compilará cuando se pegue en IDEA si tiene instalado Java 7. Hablaremos de las razones de esto al final de la conferencia. En pocas palabras, el trabajo de las clases locales depende en gran medida de la versión del idioma. Si este código no se compila automáticamente, puede cambiar la versión del idioma en IDEA a Java 8 o agregar una palabra finalal parámetro del método para que se vea así: validatePhoneNumber(final String number). Después de esto todo funcionará. Este es un pequeño programa: un validador de números de teléfono. Su método validatePhoneNumber()toma una cadena como entrada y determina si es un número de teléfono. Y dentro de este método declaramos nuestra clase local PhoneNumber. Quizás tengas una pregunta lógica: ¿por qué? ¿Por qué declarar una clase dentro de un método? ¿Por qué no utilizar una clase interna normal? De hecho, se podría hacer esto: hacer que la clase PhoneNumbersea interna. Otra cosa es que la decisión final depende de la estructura y propósito de tu programa. Recordemos nuestro ejemplo de la conferencia sobre clases internas:
public class Bicycle {

   private String model;
   private int mawWeight;

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

   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!");
       }
   }
}
En él hicimos HandleBar(el manillar) una clase interna de la bicicleta. ¿Cual es la diferencia? En primer lugar, en el uso de la clase. La clase HandleBardel segundo ejemplo es una entidad más compleja que la PhoneNumberdel primero. Primero, y HandleBartiene métodos públicos righty left(no son establecedores ni captadores). En segundo lugar, no podemos predecir de antemano dónde Bicyclelo necesitaremos y su clase externa; pueden ser docenas de lugares y métodos diferentes, incluso dentro del mismo programa. Pero con una clase PhoneNumbertodo es mucho más sencillo. Nuestro programa es muy sencillo. Sólo tiene una función: comprobar si el número es un número de teléfono. En la mayoría de los casos, el nuestro PhoneNumberValidatorni siquiera será un programa independiente, sino simplemente una parte de la lógica de autorización del programa principal. Por ejemplo, en varios sitios web, al registrarse, a menudo se le solicita que ingrese un número de teléfono. Y si escribe alguna tontería en lugar de números, el sitio arrojará un error: "¡Esto no es un número de teléfono!" Para el funcionamiento de dicho sitio (o más bien, el mecanismo de autorización del usuario), sus desarrolladores pueden incluir un análogo del nuestro en el código PhoneNumberValidator. En otras palabras, tenemos una clase externa con un método que se usará en un lugar del programa y en ningún otro. Y si es así, nada cambiará: un método hace su trabajo, eso es todo. En este caso, dado que toda la lógica de trabajo se recopila en un método, será mucho más conveniente y correcto encapsular allí una clase adicional. No tiene métodos propios además de getter y setter. Básicamente, solo necesitamos los datos del constructor. No se utiliza en otros métodos. Por lo tanto, no hay razón para ampliar la información al respecto más allá del único método donde se utiliza. Dimos un ejemplo de cómo declarar una clase local en un método, pero esta no es la única posibilidad. Se puede declarar simplemente en un bloque de código:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {


       //...código валидации номера
   }
}
¡O incluso en un bucle for!
public class PhoneNumberValidator {


   public void validatePhoneNumber(String phoneNumber) {

       for (int i = 0; i < 10; i++) {

           class PhoneNumber {

               private String phoneNumber;

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

           //...Cómoая-то логика
       }

       //...código валидации номера
   }
}
Pero estos casos son extremadamente raros. En la mayoría de los casos, la declaración seguirá produciéndose dentro del método. Entonces, nos ocupamos del anuncio, también hablamos de “filosofía” :) ¿Qué otras características y diferencias tienen las clases locales de las internas? No se puede crear un objeto de clase local fuera del método o bloque en el que se declara. Imaginemos que necesitamos un método generatePhoneNumber()que genere un número de teléfono aleatorio y devuelva un archivo PhoneNumber. No podremos crear dicho método en nuestra clase de validador en la situación actual:
public class PhoneNumberValidator {

   public void validatePhoneNumber(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;
           }
       }

       //...código валидации номера
   }

   //ошибка! компилятор не понимает, что это за класс - PhoneNumber
   public PhoneNumber generatePhoneNumber() {

   }

}
Otra característica importante de las clases locales es la capacidad de acceder a variables locales y parámetros de métodos. En caso de que lo hayas olvidado, "local" es una variable declarada dentro de un método. String russianCountryCodeEs decir, si creamos una variable local dentro de un método para algunos de nuestros propósitos validatePhoneNumber(), podemos acceder a ella desde la clase local PhoneNumber. Sin embargo, aquí hay muchas sutilezas que dependen de la versión del lenguaje utilizado en el programa. Al comienzo de la conferencia, tomamos nota de que es posible que el código de uno de los ejemplos no se compile en Java 7, ¿recuerdas? Ahora veamos las razones de esto :) En Java 7, una clase local solo puede acceder a una variable local o parámetro de método si están declarados en el método como final:
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

       //ошибка! параметр метода должен быть объявлен Cómo final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           //ошибка! локальная переменная должна быть объявлена Cómo final!
           System.out.println(russianCountryCode);
       }

   }

   //...código валидации номера
}
Aquí el compilador arrojó dos errores. Pero aquí todo está en orden:
public void validatePhoneNumber(final String number) {

   final String russianCountryCode = "+7";

    class PhoneNumber {

       private String phoneNumber;


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

       public void printRussianCountryCode() {

           System.out.println(russianCountryCode);
       }

    }

   //...código валидации номера
}
Ahora ya sabes la razón por la cual el código al comienzo de la conferencia no se compiló: una clase local en Java 7 solo tiene acceso a los finalparámetros del método y a finallas variables locales. En Java 8, el comportamiento de las clases locales ha cambiado. En esta versión del lenguaje, la clase local tiene acceso no solo a finalvariables y parámetros locales, sino también a effective-final. Effective-finales una variable cuyo valor no ha cambiado desde la inicialización. Por ejemplo, en Java 8 podemos mostrar fácilmente una variable en la consola russianCountryCode, incluso si no lo es final. Lo principal es que no cambia su significado. En este ejemplo, todo funciona como debería:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //в Java 7 здесь была бы ошибка
           System.out.println(russianCountryCode);
       }

    }

   //...código валидации номера
}
Pero si cambiamos el valor de la variable inmediatamente después de la inicialización, el código no se compilará.
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";
  russianCountryCode = "+8";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //¡error!
           System.out.println(russianCountryCode);
       }

    }

   //...código валидации номера
}
¡Pero no en vano una clase local es un subtipo de una clase interna! También tienen puntos en común. Una clase local tiene acceso a todos los campos y métodos (incluso privados) de la clase externa: tanto estáticos como no estáticos. Por ejemplo, agreguemos un campo estático a nuestra clase de validador String phoneNumberRegex:
public class PhoneNumberValidator {

   private static String phoneNumberRegex = "[^0-9]";

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
La validación se realizará utilizando esta variable estática. El método comprueba si la cadena que se le pasa contiene caracteres que no coinciden con la expresión regular " [^0-9]" (es decir, el carácter no es un número del 0 al 9). Podemos acceder fácilmente a esta variable desde la clase local PhoneNumber. Por ejemplo, escriba un captador:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
Las clases locales son similares a las clases internas porque no pueden definir ni declarar ningún miembro estático. Las clases locales en métodos estáticos solo pueden hacer referencia a miembros estáticos de la clase adjunta. Por ejemplo, si no define una variable (campo) de la clase adjunta como estática, el compilador de Java genera un error: "No se puede hacer referencia a una variable no estática desde un contexto estático". Las clases locales no son estáticas porque tienen acceso a los miembros de instancia del bloque contenedor. Por lo tanto, no pueden contener la mayoría de los tipos de declaraciones estáticas. No puedes declarar una interfaz dentro de un bloque; Las interfaces son de naturaleza estática. Este código no se compilará:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...código валидации номера
   }
}
Pero si se declara una interfaz dentro de una clase externa, la clase PhoneNumberpuede implementarla:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...código валидации номера
   }
}
Las clases locales no pueden declarar inicializadores estáticos (bloques de inicialización) o interfaces. Pero las clases locales pueden tener miembros estáticos, siempre que sean variables constantes ( static final). Eso es lo que son, ¡clases locales! Como puede ver, tienen muchas diferencias con las clases internas. Incluso tuvimos que sumergirnos en las características de la versión del idioma para entender cómo funcionan :) En la próxima conferencia, hablaremos sobre las clases internas anónimas, el último grupo de clases anidadas. ¡Buena suerte con sus estudios! :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION