CodeGym /Blog Java /Aleatoriu /Clasele interne într-o metodă locală
John Squirrels
Nivel
San Francisco

Clasele interne într-o metodă locală

Publicat în grup
Bună! Să vorbim despre un alt tip de clase imbricate. Vorbesc de clase locale (metoda-clase interioare locale). Înainte de a ne scufunda, trebuie mai întâi să ne amintim locul lor în structura claselor imbricate. Clasele interioare într-o metodă locală - 2Din diagrama noastră, putem vedea că clasele locale sunt o subspecie a claselor interioare, despre care am vorbit în detaliu în materialele anterioare . Cu toate acestea, clasele locale au o serie de caracteristici și diferențe importante față de clasele interioare obișnuite. Principalul lucru este în declarația lor: o clasă locală este declarată doar într-un bloc de cod. Cel mai adesea, această declarație se află în interiorul unei metode din clasa exterioară. De exemplu, ar putea arăta astfel:

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
   }
}
IMPORTANT!Dacă aveți Java 7 instalat, acest cod nu se va compila când este lipit în IDEA. Vom vorbi despre motivele acestui lucru la sfârșitul lecției. Pe scurt, modul în care funcționează cursurile locale depinde în mare măsură de versiunea limbii. Dacă acest cod nu se compila pentru dvs., puteți fie să comutați versiunea de limbă în IDEA la Java 8, fie să adăugați cuvântul finalla parametrul metodei astfel încât să arate astfel: validatePhoneNumber(final String number). După aceea, totul va funcționa. Acesta este un mic program care validează numerele de telefon. Metoda sa validatePhoneNumber()ia un șir ca intrare și determină dacă este un număr de telefon. Și în cadrul acestei metode, ne-am declarat clasa locală PhoneNumber. S-ar putea să întrebați în mod rezonabil de ce. De ce anume am declara o clasă în interiorul unei metode? De ce să nu folosiți o clasă interioară obișnuită? Adevărat, am fi putut facePhoneNumberclasa o clasa interioara. Dar soluția finală depinde de structura și scopul programului tău. Să ne amintim exemplul nostru dintr-o lecție despre clasele interioare:

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!");
       }
   }
}
În ea, am făcut HandleBaro clasă interioară a bicicletei. Care este diferența? În primul rând, modul în care este folosită clasa este diferit. Clasa HandleBardin al doilea exemplu este o entitate mai complexă decât PhoneNumberclasa din primul exemplu. În primul rând, HandleBarare public rightși leftmetode (acestea nu sunt setters/getters). În al doilea rând, este imposibil să prezicem în avans unde putem avea nevoie de el și Bicycleclasa sa exterioară. Ar putea exista zeci de locuri și metode diferite, chiar și într-un singur program. Dar cu PhoneNumberclasa, totul este mult mai simplu. Programul nostru este foarte simplu. Are un singur scop: să verifice dacă un număr este un număr de telefon valid. În cele mai multe cazuri, noastrePhoneNumberValidatornici măcar nu va fi un program independent, ci mai degrabă o parte a logicii de autorizare pentru un program mai mare. De exemplu, diverse site-uri web solicită adesea un număr de telefon atunci când utilizatorii se înscriu. Dacă introduceți niște prostii în loc de numere, site-ul web va raporta o eroare: „Acesta nu este un număr de telefon!” Dezvoltatorii unui astfel de site web (sau mai degrabă, mecanismul său de autorizare a utilizatorului) pot include ceva similar cu al nostruPhoneNumberValidatorîn codul lor. Cu alte cuvinte, avem o clasă exterioară cu o singură metodă, care va fi folosită într-un singur loc în program și nicăieri altundeva. Și dacă este folosit, atunci nimic nu se va schimba în el: o metodă își face treaba - și asta este. În acest caz, deoarece toată logica este adunată într-o singură metodă, va fi mult mai convenabil și mai adecvat să încapsulați o clasă suplimentară acolo. Nu are metode proprii, cu excepția unui getter și setter. De fapt, avem nevoie doar de date de la constructor. Nu este implicat în alte metode. În consecință, nu există niciun motiv să luăm informații despre aceasta în afara singurei metode în care sunt utilizate. Am dat și un exemplu în care o clasă locală este declarată într-o metodă, dar aceasta nu este singura opțiune. Poate fi declarat pur și simplu într-un bloc de cod:

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Sau chiar în forbuclă!

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
   }
}
Dar astfel de cazuri sunt extrem de rare. În cele mai multe cazuri, declarația se va întâmpla în interiorul metodei. Deci, ne-am dat seama de declarații și am vorbit și despre „filozofie” :) Ce caracteristici și diferențe suplimentare au clasele locale în comparație cu clasele interioare? Un obiect al unei clase locale nu poate fi creat în afara metodei sau blocului în care este declarat. Imaginați-vă că avem nevoie de o generatePhoneNumber()metodă care va genera un număr de telefon aleator și va returna un PhoneNumberobiect. În situația noastră actuală, nu putem crea o astfel de metodă în clasa noastră de validare:

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() {

   }

}
O altă caracteristică importantă a claselor locale este capacitatea de a accesa variabilele locale și parametrii metodei. În cazul în care ați uitat, o variabilă declarată în interiorul unei metode este cunoscută ca variabilă „locală”. Adică, dacă creăm o String usCountryCodevariabilă locală în interiorul validatePhoneNumber()metodei oarecum, o putem accesa din PhoneNumberclasa locală. Cu toate acestea, există o mulțime de subtilități care depind de versiunea limbajului folosit în program. La începutul lecției, am observat că codul pentru unul dintre exemple poate să nu fie compilat în Java 7, vă amintiți? Acum să luăm în considerare motivele pentru aceasta :) În Java 7, o clasă locală poate accesa o variabilă locală sau un parametru de metodă numai dacă sunt declarate ca finalîn metoda:

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
}
Aici compilatorul generează două erori. Și totul este în ordine aici:

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
}
Acum știți de ce codul de la începutul lecției nu s-ar compila: în Java 7, o clasă locală are acces doar la finalparametrii metodei și finalvariabilele locale. În Java 8, comportamentul claselor locale s-a schimbat. În această versiune a limbii, o clasă locală are acces nu numai la finalvariabilele și parametrii locali, ci și la cei care sunt effective-final. Effective-finaleste o variabilă a cărei valoare nu s-a schimbat de la inițializare. De exemplu, în Java 8, putem afișa cu ușurință usCountryCodevariabila pe consolă, chiar dacă nu este final. Important este că valoarea sa nu se schimbă. În exemplul următor, totul funcționează așa cum ar trebui:

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
}
Dar dacă schimbăm valoarea variabilei imediat după inițializare, codul nu se va compila.

public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printUsCountryCode() {

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

    }

   // ...number validation code
}
Nu e de mirare că o clasă locală este o subspecie a conceptului de clasă interioară! Au și caracteristici comune. O clasă locală are acces la toate câmpurile și metodele (chiar și private) ale clasei exterioare: atât statice, cât și non-statice. De exemplu, să adăugăm un String phoneNumberRegexcâmp static la clasa noastră de validare:

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Validarea va fi efectuată folosind această variabilă statică. Metoda verifică dacă șirul transmis conține caractere care nu se potrivesc cu expresia regulată „ [^0-9]” (adică orice caracter care nu este o cifră de la 0 la 9). Putem accesa cu ușurință această variabilă din PhoneNumberclasa locală. De exemplu, scrieți un getter:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Clasele locale sunt similare cu clasele interioare, deoarece nu pot defini sau declara niciun membru static. Clasele locale din metodele statice pot face referire numai la membrii statici ai clasei care le înglobează. De exemplu, dacă nu definiți o variabilă (câmp) a clasei incluse ca fiind statică, atunci compilatorul Java generează o eroare: „Variabila non-statică nu poate fi referită dintr-un context static”. Clasele locale nu sunt statice, deoarece au acces la membrii instanței din blocul care îl încadrează. Ca rezultat, ele nu pot conține majoritatea tipurilor de declarații statice. Nu puteți declara o interfață în interiorul unui bloc: interfețele sunt în mod inerent statice. Acest cod nu compilează:

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
   }
}
Dar dacă o interfață este declarată în interiorul unei clase exterioare, PhoneNumberclasa o poate implementa:

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
   }
}
Inițializatoarele statice (blocuri de inițializare) sau interfețele nu pot fi declarate în clasele locale. Dar clasele locale pot avea membri statici, cu condiția ca acestea să fie variabile constante ( static final). Și acum știți despre cursurile locale, oameni buni! După cum puteți vedea, au multe diferențe față de clasele interioare obișnuite. A trebuit chiar să analizăm caracteristicile versiunilor specifice ale limbii pentru a înțelege cum funcționează acestea :) În lecția următoare, vom vorbi despre clasele interne anonime — ultimul grup de clase imbricate. Mult succes la studii! :)
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION