CodeGym /Java блог /Случаен /Вътрешни класове в локален метод
John Squirrels
Ниво
San Francisco

Вътрешни класове в локален метод

Публикувано в групата
здрасти Нека поговорим за друг вид вложени класове. Говоря за локални класове (метод-локални вътрешни класове). Преди да се потопим, първо трябва да запомним тяхното място в структурата на вложените класове. Вътрешни занятия по локален метод – 2 брОт нашата диаграма можем да видим, че локалните класове са подвид на вътрешните класове, за които говорихме подробно в предишни материали . Въпреки това, локалните класове имат редица важни характеристики и разлики от обикновените вътрешни класове. Основното е в тяхната декларация: Локален клас се декларира само в блок code. Най-често тази декларация е вътре в няHowъв метод на външния клас. Например може да изглежда така:

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;
           }
       }

       // ...number validation code
   }
}
ВАЖНО!Ако имате инсталирана Java 7, този code няма да се компorра, когато бъде поставен в IDEA. Ще говорим за причините за това в края на урока. Накратко, How работят локалните класове зависи силно от versionта на езика. Ако този code не се компorра instead of вас, можете or да превключите езиковата version в IDEA на Java 8, or да добавите думата finalкъм параметъра на метода, така че да изглежда така: validatePhoneNumber(final String number). След това всичко ще работи. Това е малка програма, която проверява телефонни номера. Неговият validatePhoneNumber()метод приема низ като вход и определя дали е телефонен номер. И вътре в този метод декларирахме нашия локален PhoneNumberклас. Може разумно да попитате защо. Защо точно трябва да декларираме клас вътре в метод? Защо не използвате обикновен вътрешен клас? Вярно, можехме да направимPhoneNumberклас вътрешен клас. Но окончателното решение зависи от структурата и преднаmeaningто на вашата програма. Нека си припомним нашия пример от урок за вътрешни класове:

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!");
   }

   public class HandleBar {

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

       public void left() {

           System.out.println("Steer left!");
       }
   }
}
В него направихме HandleBarвътрешен клас на мотора. Каква е разликата? На първо място, начинът, по който се използва класът, е различен. Класът HandleBarвъв втория пример е по-сложен обект от PhoneNumberкласа в първия пример. Първо, HandleBarима public rightи leftметоди (това не са сетери/гетери). Второ, невъзможно е да се предвиди предварително къде може да ни потрябва той и външният му Bicycleклас. Може да има десетки различни места и методи, дори в една програма. Но с PhoneNumberкласа всичко е много по-просто. Нашата програма е много проста. Има само една цел: да провери дали даден номер е валиден телефонен номер. В повечето случаи нашитеPhoneNumberValidatorдори няма да бъде самостоятелна програма, а по-скоро част от логиката за оторизация за по-голяма програма. Например различни уебсайтове често искат телефонен номер, когато потребителите се регистрират. Ако въведете няHowва глупост instead of цифри, уебсайтът ще отчете грешка: "Това не е телефонен номер!" Разработчиците на такъв уебсайт (or по-скоро неговият механизъм за оторизация на потребителите) могат да включат нещо подобно на нашияPhoneNumberValidatorв техния code. С други думи, имаме един външен клас с един метод, който ще се използва на едно място в програмата и никъде другаде. И ако се използва, тогава нищо няма да се промени в него: един метод върши своята работа - и това е всичко. В този случай, тъй като цялата логика е събрана в един метод, ще бъде много по-удобно и правилно да капсулирате допълнителен клас там. Той няма собствени методи освен геттер и сетер. Всъщност имаме нужда само от данни от конструктора. Не се включва в други методи. Съответно, няма причина да се взема информация за него извън единствения метод, където се използва. Дадохме и пример, в който локален клас е деклариран в метод, но това не е единствената опция. Може да се декларира просто в codeов блок:

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Или дори в 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;
               }
           }
          
           // ...some logic
       }

       // ...number validation code
   }
}
Но такива случаи са изключително редки. В повечето случаи декларацията ще се случи вътре в метода. И така, измислихме декларации и също говорихме за "философията" :) Какви допълнителни функции и разлики имат локалните класове в сравнение с вътрешните класове? Обект от локален клас не може да бъде създаден извън метода or блока, в който е деклариран. Представете си, че имаме нужда от generatePhoneNumber()метод, който ще генерира произволен телефонен номер и ще върне PhoneNumberобект. В настоящата ни ситуация не можем да създадем такъв метод в нашия клас валидатор:

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;
           }
       }

       // ...number validation code
   }

   // Error! The compiler does not recognize the PhoneNumber class
   public PhoneNumber generatePhoneNumber() {

   }

}
Друга важна характеристика на локалните класове е възможността за достъп до локални променливи и параметри на метода. В случай, че сте забравor, променлива, декларирана вътре в метод, е известна като "локална" променлива. Тоест, ако създадем локална String usCountryCodeпроменлива вътре в validatePhoneNumber()метода по няHowва причина, можем да получим достъп до нея от локалния PhoneNumberклас. Има обаче много тънкости, които зависят от versionта на езика, използван в програмата. В началото на урока отбелязахме, че codeът за един от примерите може да не се компorра в Java 7, помните ли? Сега нека разгледаме причините за това :) В Java 7 локален клас може да има достъп до локална променлива or параметър на метод само ако те са декларирани като finalв метода:

public void validatePhoneNumber(String number) {

   String usCountryCode = "+1";

   class PhoneNumber {

       private String phoneNumber;

       // Error! The method parameter must be declared as final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printUsCountryCode() {

           // Error! The local variable must be declared as final!
           System.out.println(usCountryCode);
       }

   }

   // ...number validation code
}
Тук компилаторът генерира две грешки. И тук всичко е наред:

public void validatePhoneNumber(final String number) {

   final String usCountryCode = "+1";

    class PhoneNumber {

       private String phoneNumber;

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

       public void printUsCountryCode() {

           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
Сега знаете защо codeът от началото на урока не се компorра: в Java 7 локалният клас има достъп само до finalпараметрите на метода и finalлокалните променливи. В Java 8 поведението на локалните класове се промени. В тази version на езика локалният клас има достъп не само до finalлокални променливи и параметри, но и до тези, които са effective-final. Effective-finalе променлива, чиято стойност не се е променила от инициализацията. Например в Java 8 можем лесно да покажем usCountryCodeпроменливата на конзолата, дори и да не е final. Важното е стойността му да не се променя. В следния пример всичко работи Howто трябва:

public void validatePhoneNumber(String number) {

  String usCountryCode = "+1";

    class PhoneNumber {

       public void printUsCountryCode() {

           // Java 7 would produce an error here
           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
Но ако променим стойността на променливата веднага след инициализацията, codeът няма да се компorра.

public void validatePhoneNumber(String number) {

  String usCountryCode = "+1";
  usCountryCode = "+8";

    class PhoneNumber {

       public void printUsCountryCode() {

           // Error!
           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
Нищо чудно, че местната класа е подвид на концепцията за вътрешната класа! Те също имат общи характеристики. Локален клас има достъп до всички (дори частни) полета и методи на външния клас: статични и нестатични. Например, нека добавим статично String phoneNumberRegexполе към нашия клас валидатор:

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Валидирането ще се извърши с помощта на тази статична променлива. Методът проверява дали предаденият низ съдържа знаци, които не съответстват на регулярния израз " [^0-9]" (т.е. всеки знак, който не е цифра от 0 до 9). Можем лесно да получим достъп до тази променлива от локалния PhoneNumberклас. Например, напишете getter:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Локалните класове са подобни на вътрешните класове, защото не могат да дефинират or декларират ниHowви статични членове. Локалните класове в статичните методи могат да препращат само към статични членове на обхващащия клас. Например, ако не дефинирате променлива (поле) на обхващащия клас като статична, тогава компилаторът на Java генерира грешка: „Нестатичната променлива не може да бъде реферирана от статичен контекст.“ Локалните класове не са статични, защото имат достъп до членовете на екземпляра в обграждащия блок. В резултат на това те не могат да съдържат повечето типове статични декларации. Не можете да декларирате интерфейс вътре в блок: интерфейсите по своята същност са статични. Този code не се компorра:

public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}
      
       class PhoneNumber implements I{
           private String phoneNumber;

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

       // ...number validation code
   }
}
Но ако интерфейсът е деклариран във външен клас, PhoneNumberкласът може да го имплементира:

public class PhoneNumberValidator {
   interface I {}
  
   public static void validatePhoneNumber(String number) {
      
       class PhoneNumber implements I{
           private String phoneNumber;

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

       // ...number validation code
   }
}
Статичните инициализатори (блокове за инициализация) or интерфейси не могат да бъдат декларирани в локални класове. Но локалните класове могат да имат статични членове, при condition че са постоянни променливи ( static final). И сега знаете за местните класове, хора! Както можете да видите, те имат много разлики от обикновените вътрешни класове. Дори трябваше да се задълбочим в характеристиките на конкретни версии на езика, за да разберем How работят :) В следващия урок ще говорим за анонимни вътрешни класове — последната група от вложени класове. Успех в учението! :)
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION