CodeGym /Java blog /Tilfældig /Indre klasser i en lokal metode
John Squirrels
Niveau
San Francisco

Indre klasser i en lokal metode

Udgivet i gruppen
Hej! Lad os tale om en anden slags indlejrede klasser. Jeg taler om lokale klasser (metode-lokale indre klasser). Før vi dykker ind, skal vi først huske deres plads i strukturen af ​​indlejrede klasser. Indre klasser i en lokal metode - 2Fra vores diagram kan vi se, at lokale klasser er en underart af indre klasser, som vi talte detaljeret om i tidligere materialer . Lokalklasser har dog en række vigtige træk og forskelle fra almindelige indre klasser. Det vigtigste er i deres erklæring: En lokal klasse er kun erklæret i en kodeblok. Oftest er denne erklæring inde i en eller anden metode i den ydre klasse. For eksempel kan det se sådan ud:

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
   }
}
VIGTIG!Hvis du har Java 7 installeret, vil denne kode ikke kompilere, når den indsættes i IDEA. Vi vil tale om årsagerne til dette i slutningen af ​​lektionen. Kort sagt, hvordan lokale klasser fungerer er meget afhængig af sprogets version. Hvis denne kode ikke kompilerer for dig, kan du enten skifte sprogversionen i IDEA til Java 8, eller tilføje ordet finaltil metodeparameteren, så den ser sådan ud: validatePhoneNumber(final String number). Derefter vil alt fungere. Dette er et lille program, der validerer telefonnumre. Dens validatePhoneNumber()metode tager en streng som input og bestemmer, om det er et telefonnummer. Og inden for denne metode erklærede vi vores lokale PhoneNumberklasse. Du kan med rimelighed spørge hvorfor. Hvorfor præcist skulle vi erklære en klasse inde i en metode? Hvorfor ikke bruge en almindelig indre klasse? Sandt nok kunne vi have lavet denPhoneNumberklasse en indre klasse. Men den endelige løsning afhænger af strukturen og formålet med dit program. Lad os huske vores eksempel fra en lektion om indre klasser:

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!");
       }
   }
}
I den lavede vi HandleBaren indre klasse af cyklen. Hvad er forskellen? Først og fremmest er den måde, klassen bruges på, forskellig. Klassen HandleBari det andet eksempel er en mere kompleks enhed end PhoneNumberklassen i det første eksempel. For det første HandleBarhar public rightog leftmetoder (disse er ikke sættere/getters). For det andet er det umuligt på forhånd at forudsige, hvor vi kan få brug for det og dets ydre Bicycleklasse. Der kan være snesevis af forskellige steder og metoder, selv i et enkelt program. Men med PhoneNumberklassen er alt meget enklere. Vores program er meget enkelt. Det har kun ét formål: at kontrollere, om et nummer er et gyldigt telefonnummer. I de fleste tilfælde er voresPhoneNumberValidatorvil ikke engang være et selvstændigt program, men snarere en del af autorisationslogikken for et større program. For eksempel beder forskellige hjemmesider ofte om et telefonnummer, når brugere tilmelder sig. Hvis du indtaster noget nonsens i stedet for tal, vil hjemmesiden rapportere en fejl: "Dette er ikke et telefonnummer!" Udviklerne af et sådant websted (eller rettere, dets brugerautorisationsmekanisme) kan inkludere noget, der ligner voresPhoneNumberValidatori deres kode. Vi har med andre ord én ydre klasse med én metode, som vil blive brugt ét sted i programmet og ingen andre steder. Og hvis det bliver brugt, så vil intet ændre sig i det: én metode gør sit job - og det er det. I dette tilfælde, fordi al logikken er samlet i én metode, vil det være meget mere praktisk og korrekt at indkapsle en ekstra klasse der. Den har ingen egne metoder undtagen en getter og setter. Faktisk har vi kun brug for data fra konstruktøren. Det er ikke involveret i andre metoder. Der er derfor ingen grund til at tage oplysninger om det uden for den eneste metode, hvor det anvendes. Vi gav også et eksempel, hvor en lokal klasse er deklareret i en metode, men dette er ikke den eneste mulighed. Det kan blot erklæres i en kodeblok:

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Eller endda i forløkken!

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
   }
}
Men sådanne tilfælde er yderst sjældne. I de fleste tilfælde vil erklæringen ske inde i metoden. Så vi fandt ud af erklæringer, og vi talte også om "filosofien" :) Hvilke yderligere funktioner og forskelle har lokale klasser i forhold til indre klasser? Et objekt af en lokal klasse kan ikke oprettes uden for den metode eller blok, hvori det er erklæret. Forestil dig, at vi har brug for en generatePhoneNumber()metode, der genererer et tilfældigt telefonnummer og returnerer et PhoneNumberobjekt. I vores nuværende situation kan vi ikke oprette en sådan metode i vores validatorklasse:

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

   }

}
Et andet vigtigt træk ved lokale klasser er muligheden for at få adgang til lokale variabler og metodeparametre. Hvis du har glemt det, er en variabel, der er erklæret i en metode, kendt som en "lokal" variabel. Det vil sige, at hvis vi opretter en lokal String usCountryCodevariabel inde i validatePhoneNumber()metoden af ​​en eller anden grund, kan vi få adgang til den fra den lokale PhoneNumberklasse. Der er dog en masse finesser, der afhænger af versionen af ​​det sprog, der bruges i programmet. I begyndelsen af ​​lektionen bemærkede vi, at koden til et af eksemplerne muligvis ikke kompileres i Java 7, husker du? Lad os nu overveje årsagerne til dette :) I Java 7 kan en lokal klasse kun få adgang til en lokal variabel eller metodeparameter, hvis de er erklæret som finali metoden:

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
}
Her genererer compileren to fejl. Og alt er i orden her:

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
}
Nu ved du hvorfor koden fra begyndelsen af ​​lektionen ikke ville kompilere: i Java 7 har en lokal klasse kun adgang til finalmetodeparametre og finallokale variabler. I Java 8 har lokale klassers adfærd ændret sig. I denne version af sproget har en lokal klasse ikke kun adgang til finallokale variabler og parametre, men også til dem, der er effective-final. Effective-finaler en variabel, hvis værdi ikke er ændret siden initialisering. For eksempel kan vi i Java 8 nemt vise usCountryCodevariablen på konsollen, selvom den ikke er final. Det vigtige er, at dens værdi ikke ændres. I følgende eksempel fungerer alt, som det skal:

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
}
Men hvis vi ændrer variablens værdi umiddelbart efter initialisering, vil koden ikke kompilere.

public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printUsCountryCode() {

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

    }

   // ...number validation code
}
Ikke underligt, at en lokal klasse er en underart af begrebet den indre klasse! De har også fælles karakteristika. En lokal klasse har adgang til alle (selv private) felter og metoder i den ydre klasse: både statiske og ikke-statiske. Lad os f.eks. tilføje et statisk String phoneNumberRegexfelt til vores validatorklasse:

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Validering vil blive udført ved hjælp af denne statiske variabel. Metoden kontrollerer, om den beståede streng indeholder tegn, der ikke matcher det regulære udtryk " [^0-9]" (det vil sige ethvert tegn, der ikke er et ciffer fra 0 til 9). Vi kan nemt få adgang til denne variabel fra den lokale PhoneNumberklasse. Skriv for eksempel en getter:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Lokale klasser ligner indre klasser, fordi de ikke kan definere eller erklære nogen statiske medlemmer. Lokale klasser i statiske metoder kan kun referere til statiske medlemmer af den omsluttende klasse. For eksempel, hvis du ikke definerer en variabel (felt) i den omsluttende klasse som statisk, genererer Java-kompileren en fejl: "Ikke-statisk variabel kan ikke refereres fra en statisk kontekst." Lokale klasser er ikke statiske, fordi de har adgang til instansmedlemmer i den omsluttende blok. Som følge heraf kan de ikke indeholde de fleste typer statiske deklarationer. Du kan ikke erklære en grænseflade inde i en blok: grænseflader er i sagens natur statiske. Denne kode kompilerer ikke:

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
   }
}
Men hvis en grænseflade er erklæret inde i en ydre klasse, PhoneNumberkan klassen implementere den:

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
   }
}
Statiske initialiseringsprogrammer (initialiseringsblokke) eller grænseflader kan ikke erklæres i lokale klasser. Men lokale klasser kan have statiske medlemmer, forudsat at de er konstante variable ( static final). Og nu ved du om lokale klasser, folkens! Som du kan se, har de mange forskelle fra almindelige indre klasser. Vi var endda nødt til at dykke ned i funktionerne i specifikke versioner af sproget for at forstå, hvordan de fungerer :) I den næste lektion vil vi tale om anonyme indre klasser — den sidste gruppe af indlejrede klasser. Held og lykke med dine studier! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION