JavaRush /Blogue Java /Random-PT /Classes internas no método local

Classes internas no método local

Publicado no grupo Random-PT
Olá! Vamos falar sobre outro tipo de classe aninhada. Ou seja, sobre classes locais (classes internas locais do método). A primeira coisa que você precisa lembrar antes de estudar é o lugar deles na estrutura das classes aninhadas. Classes internas no método local - 2Com base em nosso diagrama, podemos entender que as classes locais são um subtipo de classes internas, sobre as quais falamos detalhadamente em um dos materiais anteriores . No entanto, as classes locais têm uma série de características e diferenças importantes em relação às classes internas. A chave está na declaração deles: uma classe local é declarada apenas em um bloco de código. Na maioria das vezes - dentro de algum método de uma classe externa. Por exemplo, pode ser assim:
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;
           }
       }

       //...code валидации номера
   }
}
IMPORTANTE!Este código não será compilado quando colado no IDEA se você tiver o Java 7 instalado. Falaremos sobre os motivos disso no final da palestra. Em poucas palavras, o trabalho das classes locais é altamente dependente da versão do idioma. Se esse código não for compilado para você, você pode mudar a versão da linguagem no IDEA para Java 8 ou adicionar uma palavra finalao parâmetro do método para que fique assim: validatePhoneNumber(final String number). Depois disso tudo funcionará. Este é um pequeno programa - um validador de número de telefone. Seu método validatePhoneNumber()recebe uma string como entrada e determina se é um número de telefone. E dentro deste método declaramos nossa classe local PhoneNumber. Você pode ter uma pergunta lógica: por quê? Por que declarar uma classe dentro de um método? Por que não usar uma classe interna regular? Na verdade, alguém poderia fazer isto: tornar a classe PhoneNumberinterna. Outra coisa é que a decisão final depende da estrutura e da finalidade do seu programa. Vamos relembrar nosso exemplo da palestra sobre classes 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("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }
}
Nele fizemos HandleBar(o guidão) uma classe interna da bicicleta. Qual é a diferença? Primeiro de tudo, no uso da classe. A classe HandleBardo segundo exemplo é uma entidade mais complexa que PhoneNumbera do primeiro. Primeiro, y HandleBarpossui métodos públicos righte left(não são setter e getter). Em segundo lugar, não podemos prever antecipadamente onde Bicyclepoderemos precisar dele e da sua classe externa - podem ser dezenas de locais e métodos diferentes, mesmo dentro do mesmo programa. Mas com uma aula PhoneNumbertudo é muito mais simples. Nosso programa é muito simples. Tem apenas uma função - verificar se o número é um número de telefone. Na maioria dos casos, o nosso PhoneNumberValidatorprograma nem sequer será independente, mas simplesmente uma parte da lógica de autorização do programa principal. Por exemplo, em vários sites, ao se registrar, muitas vezes é solicitado que você insira um número de telefone. E se você digitar alguma bobagem em vez de números, o site exibirá um erro: “Este não é um número de telefone!” Para a operação de tal site (ou melhor, o mecanismo de autorização do usuário), seus desenvolvedores podem incluir um análogo do nosso no código PhoneNumberValidator. Em outras palavras, temos uma classe externa com um método que será usado em um local do programa e em nenhum outro lugar. E se isso acontecer, nada mudará: um método faz seu trabalho - isso é tudo. Nesse caso, como toda a lógica de trabalho é coletada em um método, será muito mais conveniente e correto encapsular ali uma classe adicional. Ele não possui métodos próprios além de getter e setter. Essencialmente, precisamos apenas dos dados do construtor dele. Não é usado em outros métodos. Portanto, não há razão para estender as informações sobre ele além do método único onde é usado. Demos um exemplo de declaração de uma classe local em um método, mas esta não é a única possibilidade. Pode ser declarado simplesmente em um bloco de código:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {


       //...code валидации номера
   }
}
Ou mesmo em loop 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;
               }
           }

           //...Howая-то логика
       }

       //...code валидации номера
   }
}
Mas tais casos são extremamente raros. Na maioria dos casos, a declaração ainda ocorrerá dentro do método. Então, tratamos do edital, falamos também de “filosofia” :) Que outras características e diferenças as turmas locais têm das turmas internas? Um objeto de classe local não pode ser criado fora do método ou bloco no qual é declarado. Imagine que precisamos de um método generatePhoneNumber()que gere um número de telefone aleatório e retorne um arquivo PhoneNumber. Não poderemos criar tal método em nossa classe validadora na situação atual:
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;
           }
       }

       //...code валидации номера
   }

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

   }

}
Outra característica importante das classes locais é a capacidade de acessar variáveis ​​locais e parâmetros de métodos. Caso você tenha esquecido, “local” é uma variável declarada dentro de um método. String russianCountryCodeOu seja, se criarmos uma variável local dentro de um método para alguns de nossos propósitos validatePhoneNumber(), podemos acessá-la a partir da classe local PhoneNumber. No entanto, existem muitas sutilezas aqui que dependem da versão da linguagem usada no programa. No início da palestra, anotamos que o código de um dos exemplos pode não ser compilado em Java 7, lembra? Agora vamos ver as razões para isso :) Em Java 7, uma classe local só pode acessar uma variável local ou parâmetro de método se eles forem declarados no método como final:
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

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

       public void printRussianCountryCode() {

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

   }

   //...code валидации номера
}
Aqui o compilador gerou dois erros. Mas aqui está tudo em ordem:
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);
       }

    }

   //...code валидации номера
}
Agora você sabe o motivo pelo qual o código do início da palestra não foi compilado: uma classe local em Java 7 só tem acesso aos finalparâmetros -method e finalàs variáveis ​​-local. No Java 8, o comportamento das classes locais mudou. Nesta versão da linguagem, a classe local tem acesso não apenas a finalvariáveis ​​e parâmetros -locais, mas também a effective-final. Effective-finalé uma variável cujo valor não mudou desde a inicialização. Por exemplo, em Java 8 podemos facilmente exibir uma variável no console russianCountryCode, mesmo que não seja final. O principal é que não muda o seu significado. Neste exemplo, tudo funciona como deveria:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

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

    }

   //...code валидации номера
}
Mas se alterarmos o valor da variável imediatamente após a inicialização, o código não será compilado.
public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printRussianCountryCode() {

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

    }

   //...code валидации номера
}
Mas não é à toa que uma classe local é um subtipo de uma classe interna! Eles também têm pontos em comum. Uma classe local tem acesso a todos os campos e métodos (até mesmo privados) da classe externa: estáticos e não estáticos. Por exemplo, vamos adicionar um campo estático à nossa classe validadora String phoneNumberRegex:
public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
A validação será realizada usando esta variável estática. O método verifica se a string passada para ele contém caracteres que não correspondem à expressão regular " [^0-9]" (ou seja, o caractere não é um número de 0 a 9). Podemos acessar facilmente essa variável na classe local PhoneNumber. Por exemplo, escreva um getter:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
As classes locais são semelhantes às classes internas porque não podem definir ou declarar nenhum membro estático. As classes locais em métodos estáticos só podem fazer referência a membros estáticos da classe envolvente. Por exemplo, se você não definir uma variável (campo) da classe envolvente como estática, o compilador Java gerará um erro: “Uma variável não estática não pode ser referenciada a partir de um contexto estático”. As classes locais não são estáticas porque têm acesso aos membros da instância do bloco que as contém. Portanto, eles não podem conter a maioria dos tipos de declarações estáticas. Você não pode declarar uma interface dentro de um bloco; As interfaces são de natureza estática. Este código não será compilado:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...code валидации номера
   }
}
Mas se uma interface for declarada dentro de uma classe externa, a classe PhoneNumberpoderá implementá-la:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...code валидации номера
   }
}
As classes locais não podem declarar inicializadores estáticos (blocos de inicialização) ou interfaces. Mas as classes locais podem ter membros estáticos, desde que sejam variáveis ​​constantes ( static final). É isso que são, aulas locais! Como você pode ver, eles têm muitas diferenças em relação às classes internas. Tivemos até que nos aprofundar nos recursos da versão da linguagem para entender como elas funcionam :) Na próxima aula falaremos sobre classes internas anônimas - o último grupo de classes aninhadas. Boa sorte com seus estudos! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION