JavaRush /Java блогу /Random-KY /Жергиликтүү метод боюнча ички класстар

Жергиликтүү метод боюнча ички класстар

Группада жарыяланган
Салам! Уяланган класстын дагы бир түрү жөнүндө сүйлөшөлү. Тактап айтканда, жергorктүү класстар жөнүндө (Метод жергorктүү ички класстар). Окуудан мурун эстен чыгарбоо керек болгон биринчи нерсе, алардын уя класстардын структурасындагы орду. Жергorктүү метод боюнча ички класстар - 2Диаграммабыздын негизинде биз жергorктүү класстар ички класстардын бир түрү экенин түшүнсөк болот, алар жөнүндө мурунку материалдардын биринде кеңири сөз кылганбыз . Бирок жергorктүү класстар ички класстардан бир катар маанилүү өзгөчөлүктөргө жана айырмачылыктарга ээ. Ачкыч алардын декларациясында: Локалдык класс code блогунда гана жарыяланат. Көбүнчө - тышкы класстын кандайдыр бир ыкмасынын ичинде. Мисалы, мындай көрүнүшү мүмкүн:
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 валидации номера
   }
}
МААНИЛҮҮ!Эгер сизде Java 7 орнотулган болсо, бул code IDEAга чапталганда компиляцияланbyte. Мунун себептери тууралуу лекциянын аягында сүйлөшөбүз. Бир нече сөз менен айтканда, жергorктүү класстардын иши тил versionсына абдан көз каранды. Эгер бул code сиз үчүн компиляцияланбаса, сиз IDEAдагы тил versionсын Java 8ге которсоңуз болот же finalметод параметрине сөздү кошсоңуз болот, ал төмөнкүдөй көрүнөт: validatePhoneNumber(final String number). Ушундан кийин баары иштейт. Бул кичинекей программа - телефон номерин текшерүүчү. Анын ыкмасы validatePhoneNumber()киргизүү катары сапты алып, анын телефон номери экендигин аныктайт. Жана бул ыкманын ичинде биз жергorктүү классыбызды жарыяладык PhoneNumber. Сизде логикалык суроо болушу мүмкүн: эмне үчүн? Эмне үчүн классты методдун ичинде жарыялоо керек? Эмне үчүн кадимки ички классты колдонбойт? Чынында эле, муну кылса болот: классты PhoneNumberички кыл. Дагы бир нерсе, акыркы чечим сиздин программаңыздын структурасына жана максатына жараша болот. Ички класстар жөнүндөгү лекциядагы мисалыбызды эстейли:
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!");
       }
   }
}
Анда биз HandleBar(рулду) велосипеддин ички классын жасадык. Айырмасы эмне? Биринчиден, классты колдонууда. Экинчи мисалдагы класс биринчисине HandleBarкараганда татаалыраак нерсе . PhoneNumberБиринчиден, y HandleBarкоомдук ыкмалары бар rightжана left(сетер жана алуучу эмес). Экинчиден, биз аны жана анын тышкы классын каякка керектелерин алдын ала айта албайбыз Bicycle- булар бир эле программанын ичинде ондогон ар кандай жерлер жана методдор болушу мүмкүн. Бирок класс менен PhoneNumberбаары алда канча жөнөкөй. Биздин программа абдан жөнөкөй. Анын бир гана функциясы бар - номер телефон номери экенин текшерүү. Көпчүлүк учурларда, биздики PhoneNumberValidatorкөз карандысыз программа эмес, жөн гана негизги программа үчүн авторизациялоо логикасынын бир бөлүгү болуп калат. Мисалы, ар кандай веб-сайттарда каттоодон өткөндө телефон номерин киргизүүнү суранышат. Эгер сиз сандардын ордуна кандайдыр бир маанисиз сөздөрдү жазсаңыз, сайт катаны чыгарат: "Бул телефон номери эмес!" Мындай сайттын иштеши үчүн (тактап айтканда, колдонуучунун авторизация механизми), аны иштеп чыгуучулар codeго биздин аналогубузду киргизе алышат PhoneNumberValidator. Башкача айтканда, бизде программанын бир жеринде жана башка эч жерде колдонула турган бир методу бар бир тышкы класс бар. Эгер ошондой болсо, анда анда эч нерсе өзгөрбөйт: бир ыкма өз милдетин аткарат - бардыгы. Бул учурда, бардык иш логикасы бир ыкмада чогултулгандыктан, ал жерде кошумча классты капсулдаштыруу алда канча ыңгайлуу жана туура болот. Анын гетер жана сетерден башка өзүнүн ыкмалары жок. Бизге негизинен андан конструктор маалыматтары гана керек. Башка ыкмаларда колдонулbyte. Ошондуктан, ал жөнүндө маалыматты ал колдонулган жалгыз ыкмадан тышкары кеңейтүүгө эч кандай негиз жок. Биз методдо жергorктүү классты жарыялоонун мисалын келтирдик, бирок бул жалгыз мүмкүнчүлүк эмес. Аны жөн гана code блогунда жарыялоого болот:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {


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

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

       //...code валидации номера
   }
}
Бирок мындай учурлар өтө сейрек кездешет. Көпчүлүк учурларда, декларация дагы эле ыкманын ичинде пайда болот. Ошентип, биз кулактандыруу менен алектендик, биз дагы "философия" жөнүндө сүйлөштүк :) Жергorктүү класстардын ички класстардан дагы кандай өзгөчөлүктөрү жана айырмачылыктары бар? Жергorктүү класстын an objectисин ал жарыяланган ыкмадан же блоктон тышкары түзүүгө болбойт. 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;
           }
       }

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

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

   }

}
Локалдык класстардын дагы бир маанилүү өзгөчөлүгү - локалдык өзгөрмөлөргө жана метод параметрлерине кирүү мүмкүнчүлүгү. Эгер сиз унутуп калсаңыз, "локалдык" бул ыкманын ичинде жарыяланган өзгөрмө. Башкача айтканда, кээ бир максаттарыбыз үчүн String russianCountryCodeметоддун ичинде жергorктүү өзгөрмө түзсөк validatePhoneNumber(), ага жергorктүү класстан кире алабыз PhoneNumber. Бирок, бул жерде программада колдонулган тилдин versionсына жараша бир топ кылдаттыктар бар. Лекциянын башында биз мисалдардын бириндеги code Java 7де компиляцияланбашы мүмкүн экенин белгилегенбиз, эсиңиздеби? Эми мунун себептерин карап көрөлү :) Java 7де локалдык класс локалдык өзгөрмөгө же метод параметрине кире алат, эгерде алар методдо мындай деп жарыя кылынса 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 валидации номера
}
Бул жерде компилятор эки ката кетирди. Бирок бул жерде баары өз тартибинде:
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 валидации номера
}
Эми сиз лекциянын башында code түзүлбөй калганынын себебин билесиз: Java 7деги локалдык класс final-методдун параметрлерине жана final-локалдык өзгөрмөлөргө гана кире алат. Java 8де жергorктүү класстардын жүрүм-туруму өзгөрдү. Тилдин бул versionсында локалдык класс final-локалдык өзгөрмөлөргө жана параметрлерге гана эмес, effective-final. Effective-finalинициализациядан бери мааниси өзгөрбөгөн өзгөрмө. Мисалы, Java 8де биз өзгөрмөлөрдү консолго оңой көрсөтө алабыз russianCountryCode, ал болбосо да final. Эң негизгиси анын маанисин өзгөртпөйт. Бул мисалда, баары болушу керек эле иштейт:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

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

    }

   //...code валидации номера
}
Бирок биз инициализациядан кийин өзгөрмөнүн маанисин дароо өзгөртсөк, code түзүлбөйт.
public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printRussianCountryCode() {

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

    }

   //...code валидации номера
}
Бирок жергorктүү класс ички класстын бир түрү деп бекеринен айтылган эмес! Алардын орток жактары да бар. Жергorктүү класс тышкы класстын бардык (ал тургай жеке) талааларына жана ыкмаларына кире алат: статикалык жана статикалык эмес. Мисалы, биздин валидатор классыбызга статикалык талааны кошолу String phoneNumberRegex:
public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
Текшерүү бул статикалык өзгөрмө аркылуу аткарылат. Метод ага берилген сапта кадимки " " сөз айкашына дал келбеген символдор бар же жок экенин текшерет [^0-9](б.а. символ 0дөн 9га чейинки сан эмес). Биз бул өзгөрмөгө жергorктүү класстан оңой жете алабыз PhoneNumber. Мисалы, алуучуну жазыңыз:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
Жергorктүү класстар ички класстарга окшош, анткени алар кандайдыр бир статикалык мүчөлөрдү аныктай же жарыялай алbyte. Статикалык методдордогу локалдык класстар курчап турган класстын статикалык мүчөлөрүнө гана кайрыла алат. Мисалы, эгер сиз курчап турган класстын өзгөрмөсүн (талаасын) статикалык деп аныктабасаңыз, Java компилятору катаны жаратат: "Статикалык эмес өзгөрмөгө статикалык контексттен шилтеме жасоо мүмкүн эмес." Жергorктүү класстар статикалык эмес, анткени алар камтыган блоктун инстанция мүчөлөрүнө кирүү мүмкүнчүлүгүнө ээ. Ошондуктан, алар статикалык декларациялардын көпчүлүк түрлөрүн камтый алbyte. Блоктун ичиндеги интерфейсти жарыялай албайсыз; Интерфейстердин табияты статикалык. Бул code түзүлбөйт:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

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

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

       //...code валидации номера
   }
}
Жергorктүү класстар статикалык инициализаторлорду (инициализация блокторун) же интерфейстерди жарыялай алbyte. Бирок жергorктүү класстар туруктуу өзгөрмөлөр ( ) болгон шартта статикалык мүчөлөргө ээ болушу мүмкүн static final. Булар — жергorктуу класстар! Көрүнүп тургандай, алардын ички класстардан көптөгөн айырмачылыктары бар. Ал тургай, алардын кантип иштээрин түшүнүү үчүн тил versionсынын өзгөчөлүктөрүнө сүңгүп чыгууга туура келди :) Кийинки лекцияда биз анонимдүү ички класстар - уя класстардын акыркы тобу жөнүндө сүйлөшөбүз. Сиздин изилдөө менен ийгorк коштосун! :)
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION