CodeGym /Java-blogg /Tilfeldig /Indre klasser i en lokal metode
John Squirrels
Nivå
San Francisco

Indre klasser i en lokal metode

Publisert i gruppen
Hei! La oss snakke om en annen type nestede klasser. Jeg snakker om lokale klasser (metode-lokale indre klasser). Før vi dykker inn, må vi først huske deres plass i strukturen til nestede klasser. Indre klasser i en lokal metode - 2Fra diagrammet vårt kan vi se at lokale klasser er en underart av indre klasser, som vi snakket om i detalj i tidligere materialer . Lokalklasser har imidlertid en rekke viktige trekk og forskjeller fra vanlige indre klasser. Det viktigste er i erklæringen deres: En lokal klasse er bare deklarert i en kodeblokk. Oftest er denne erklæringen inne i en eller annen metode i den ytre klassen. For eksempel kan det se slik ut:

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
   }
}
VIKTIG!Hvis du har Java 7 installert, vil ikke denne koden kompileres når den limes inn i IDEA. Vi vil snakke om årsakene til dette på slutten av leksjonen. Kort sagt, hvordan lokale klasser fungerer er svært avhengig av språkversjonen. Hvis denne koden ikke kompilerer for deg, kan du enten bytte språkversjonen i IDEA til Java 8, eller legge til ordet finali metodeparameteren slik at den ser slik ut: validatePhoneNumber(final String number). Etter det vil alt fungere. Dette er et lite program som validerer telefonnumre. Metoden validatePhoneNumber()tar en streng som input og bestemmer om det er et telefonnummer. Og inne i denne metoden erklærte vi vår lokale PhoneNumberklasse. Du kan med rimelighet spørre hvorfor. Hvorfor skal vi deklarere en klasse i en metode? Hvorfor ikke bruke en vanlig indre klasse? Riktignok kunne vi ha lagetPhoneNumberklasse en indre klasse. Men den endelige løsningen avhenger av strukturen og formålet med programmet ditt. La oss huske vårt eksempel fra en leksjon 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 laget vi HandleBaren indre klasse av sykkelen. Hva er forskjellen? For det første er måten klassen brukes på forskjellig. Klassen HandleBari det andre eksemplet er en mer kompleks enhet enn PhoneNumberklassen i det første eksemplet. For det første HandleBarhar offentlige rightog leftmetoder (disse er ikke settere/getters). For det andre er det umulig å forutsi på forhånd hvor vi kan trenge den og dens ytre Bicycleklasse. Det kan være dusinvis av forskjellige steder og metoder, selv i et enkelt program. Men med PhoneNumberklassen er alt mye enklere. Vårt program er veldig enkelt. Den har bare ett formål: å sjekke om et nummer er et gyldig telefonnummer. I de fleste tilfeller er vårPhoneNumberValidatorvil ikke engang være et frittstående program, men snarere en del av autorisasjonslogikken for et større program. For eksempel spør ulike nettsteder ofte om et telefonnummer når brukere registrerer seg. Hvis du skriver inn noe tull i stedet for tall, vil nettsiden rapportere en feil: "Dette er ikke et telefonnummer!" Utviklerne av et slikt nettsted (eller rettere sagt, dets brukerautorisasjonsmekanisme) kan inkludere noe som ligner på vårPhoneNumberValidatori koden deres. Vi har med andre ord én ytre klasse med én metode, som skal brukes ett sted i programmet og ingen andre steder. Og hvis den brukes, vil ingenting endre seg i den: én metode gjør jobben sin - og det er det. I dette tilfellet, fordi all logikken er samlet i én metode, vil det være mye mer praktisk og riktig å kapsle inn en ekstra klasse der. Den har ingen egne metoder bortsett fra en getter og setter. Faktisk trenger vi bare data fra konstruktøren. Det er ikke involvert i andre metoder. Følgelig er det ingen grunn til å ta informasjon om det utenfor den eneste metoden der det brukes. Vi ga også et eksempel der en lokal klasse er deklarert i en metode, men dette er ikke det eneste alternativet. Det kan deklareres ganske enkelt i en kodeblokk:

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Eller til og med 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 slike tilfeller er ekstremt sjeldne. I de fleste tilfeller vil erklæringen skje inne i metoden. Så, vi fant ut erklæringer, og vi snakket også om "filosofien" :) Hvilke tilleggstrekk og forskjeller har lokale klasser i forhold til indre klasser? Et objekt av en lokal klasse kan ikke opprettes utenfor metoden eller blokken den er deklarert i. Tenk deg at vi trenger en generatePhoneNumber()metode som vil generere et tilfeldig telefonnummer og returnere et PhoneNumberobjekt. I vår nåværende situasjon kan vi ikke lage en slik metode i validatorklassen vår:

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 annet viktig trekk ved lokale klasser er muligheten til å få tilgang til lokale variabler og metodeparametere. I tilfelle du har glemt det, er en variabel deklarert i en metode kjent som en "lokal" variabel. Det vil si at hvis vi lager en lokal String usCountryCodevariabel inne i validatePhoneNumber()metoden av en eller annen grunn, kan vi få tilgang til den fra den lokale PhoneNumberklassen. Imidlertid er det mange finesser som avhenger av versjonen av språket som brukes i programmet. I begynnelsen av leksjonen la vi merke til at koden for et av eksemplene kanskje ikke kompileres i Java 7, husker du? La oss nå vurdere årsakene til dette :) I Java 7 kan en lokal klasse bare få tilgang til en lokal variabel eller metodeparameter hvis de er deklarert 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 kompilatoren to feil. 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
}
Nå vet du hvorfor koden fra begynnelsen av leksjonen ikke ville kompileres: i Java 7 har en lokal klasse bare tilgang til finalmetodeparametere og finallokale variabler. I Java 8 har oppførselen til lokale klasser endret seg. I denne versjonen av språket har en lokal klasse tilgang ikke bare til finallokale variabler og parametere, men også til de som er effective-final. Effective-finaler en variabel hvis verdi ikke har endret seg siden initialisering. For eksempel, i Java 8, kan vi enkelt vise usCountryCodevariabelen på konsollen, selv om den ikke er final. Det viktigste er at verdien ikke endres. 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 endrer variabelens verdi umiddelbart etter initialisering, vil ikke koden kompileres.

public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printUsCountryCode() {

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

    }

   // ...number validation code
}
Ikke rart en lokal klasse er en underart av begrepet indre klasse! De har også felles kjennetegn. En lokal klasse har tilgang til alle (selv private) felt og metoder for den ytre klassen: både statiske og ikke-statiske. La oss for eksempel legge til et statisk String phoneNumberRegexfelt i validatorklassen vår:

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Validering vil bli utført ved å bruke denne statiske variabelen. Metoden sjekker om den beståtte strengen inneholder tegn som ikke samsvarer med det regulære uttrykket " [^0-9]" (det vil si ethvert tegn som ikke er et siffer fra 0 til 9). Vi kan enkelt få tilgang til denne variabelen fra den lokale PhoneNumberklassen. Skriv for eksempel en getter:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Lokale klasser ligner på indre klasser, fordi de ikke kan definere eller deklarere noen statiske medlemmer. Lokale klasser i statiske metoder kan bare referere til statiske medlemmer av den omsluttende klassen. For eksempel, hvis du ikke definerer en variabel (felt) i den omsluttende klassen som statisk, genererer Java-kompilatoren en feil: "Ikke-statisk variabel kan ikke refereres fra en statisk kontekst." Lokale klasser er ikke statiske, fordi de har tilgang til instansmedlemmer i den omsluttende blokken. Som et resultat kan de ikke inneholde de fleste typer statiske deklarasjoner. Du kan ikke deklarere et grensesnitt inne i en blokk: grensesnitt er iboende statiske. Denne koden 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 et grensesnitt er deklarert inne i en ytre klasse, PhoneNumberkan klassen implementere det:

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 initialisatorer (initialiseringsblokker) eller grensesnitt kan ikke deklareres i lokale klasser. Men lokale klasser kan ha statiske medlemmer, forutsatt at de er konstante variabler ( static final). Og nå vet du om lokale klasser, folkens! Som du ser har de mange forskjeller fra vanlige indre klasser. Vi måtte til og med fordype oss i funksjonene til spesifikke versjoner av språket for å forstå hvordan de fungerer :) I neste leksjon skal vi snakke om anonyme indre klasser — den siste gruppen av nestede klasser. Lykke til i studiene! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION