CodeGym /Java blog /Véletlen /Belső osztályok helyi módszerrel
John Squirrels
Szint
San Francisco

Belső osztályok helyi módszerrel

Megjelent a csoportban
Szia! Beszéljünk egy másik típusú beágyazott osztályról. A helyi osztályokról beszélek (módszer-helyi belső osztályok). Mielőtt belemerülnénk, először meg kell emlékeznünk a beágyazott osztályok szerkezetében elfoglalt helyükről. Belső osztályok helyi módszerrel - 2Diagramunkból láthatjuk, hogy a lokális osztályok a belső osztályok alfaja, amelyről az előző anyagokban részletesen beszéltünk . A helyi osztályok azonban számos fontos jellemzővel és különbséggel rendelkeznek a hagyományos belső osztályoktól. A lényeg a deklarációjukban van: Egy helyi osztály csak egy kódblokkban van deklarálva. Leggyakrabban ez a deklaráció a külső osztály valamelyik metódusán belül található. Például így nézhet ki:

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
   }
}
FONTOS!Ha telepítve van a Java 7, akkor ez a kód nem fordítódik le, amikor beilleszti az IDEA-ba. Ennek okairól a lecke végén beszélünk. Röviden, a helyi osztályok működése nagymértékben függ a nyelv verziójától. Ha ez a kód nem fordul le Ön helyett, az IDEA nyelvi verzióját átválthatja Java 8-ra, vagy hozzáadhatja a szót finala metódus paraméteréhez, hogy így nézzen ki: validatePhoneNumber(final String number). Utána minden működni fog. Ez egy kis program, amely hitelesíti a telefonszámokat. A validatePhoneNumber()metódusa egy karakterláncot vesz fel bemenetként, és meghatározza, hogy telefonszám-e. És ezen a metóduson belül deklaráltuk a helyi osztályunkat PhoneNumber. Jogosan kérdezheti, hogy miért. Miért deklarálunk egy osztályt egy metóduson belül? Miért nem használ egy közönséges belső osztályt? Igaz, elkészíthettük volna aPhoneNumberosztály egy belső osztály. De a végső megoldás a program felépítésétől és céljától függ. Idézzük fel példánkat a belső osztályokról szóló leckéből:

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!");
       }
   }
}
Ebben elkészítettük HandleBara kerékpár belső osztályát. Mi a különbség? Először is, az osztály használatának módja más. A HandleBarmásodik példában szereplő osztály összetettebb entitás, mint az PhoneNumberelső példában szereplő osztály. Először is, HandleBarpublikus rightés leftmetódusai vannak (ezek nem setterek/getterek). Másodszor, lehetetlen előre megjósolni, hol lehet szükségünk rá és a külső osztályára Bicycle. Több tucat különböző hely és módszer lehet, akár egyetlen programban is. De az osztálynál PhoneNumberminden sokkal egyszerűbb. Programunk nagyon egyszerű. Egyetlen célja van: annak ellenőrzése, hogy egy szám érvényes telefonszám-e. A legtöbb esetben a miPhoneNumberValidatormég csak nem is önálló program lesz, hanem egy nagyobb program engedélyezési logikájának része. Például különböző webhelyek gyakran kérnek telefonszámot, amikor a felhasználók regisztrálnak. Ha számok helyett hülyeségeket adsz meg, a weboldal hibát jelez: "Ez nem telefonszám!" Egy ilyen webhely fejlesztői (vagy inkább felhasználói engedélyezési mechanizmusa) tartalmazhatnak valami hasonlót, mint a miénkPhoneNumberValidatorkódjukban. Más szóval, van egy külső osztályunk egy metódussal, amelyet a programban egy helyen fogunk használni, és sehol máshol. És ha használják, akkor semmi sem fog változni benne: egy módszer elvégzi a dolgát – és ennyi. Ebben az esetben, mivel az összes logika egyetlen metódusban van összegyűjtve, sokkal kényelmesebb és helyénvalóbb egy további osztályt beágyazni. Nincsenek saját módszerei, kivéve a gettert és a settert. Valójában csak a konstruktor adataira van szükségünk. Más módszerekben nem vesz részt. Ennek megfelelően nincs ok arra, hogy az ezzel kapcsolatos információkat az egyetlen módszeren kívülre vegyük, ahol használják. Adtunk egy példát arra is, hogy egy lokális osztály deklarálva van egy metódusban, de nem ez az egyetlen lehetőség. Egyszerűen deklarálható egy kódblokkban:

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Vagy akár a forhurokban!

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
   }
}
De az ilyen esetek rendkívül ritkák. A legtöbb esetben a deklaráció a metóduson belül történik. Szóval kitaláltuk a deklarációkat, és beszéltünk a "filozófiáról" is :) Milyen további jellemzők, különbségek vannak a helyi osztályoknak a belső osztályokhoz képest? Egy helyi osztály objektuma nem hozható létre azon a metóduson vagy blokkon kívül, amelyben deklarálva van. Képzeljük el, hogy szükségünk van egy generatePhoneNumber()metódusra, amely véletlenszerű telefonszámot generál, és egy PhoneNumberobjektumot ad vissza. Jelenlegi helyzetünkben nem tudunk ilyen metódust létrehozni a validátor osztályunkban:

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

   }

}
A helyi osztályok másik fontos jellemzője a helyi változók és metódusparaméterek elérésének képessége. Ha elfelejtette volna, a metóduson belül deklarált változót „helyi” változónak nevezik. Vagyis ha valamilyen okból létrehozunk egy lokális String usCountryCodeváltozót a validatePhoneNumber()metóduson belül, akkor azt a lokális osztályból érhetjük el PhoneNumber. Számos finomság azonban a programban használt nyelv verziójától függ. A lecke elején megjegyeztük, hogy előfordulhat, hogy az egyik példa kódja nem fordítható le Java 7-ben, emlékszel? Most nézzük meg ennek okait :) Java 7-ben egy helyi osztály csak akkor tud hozzáférni egy helyi változóhoz vagy metódus paraméterhez, ha azok finala metódusban megadottak szerint vannak deklarálva:

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
}
Itt a fordító két hibát generál. És itt minden rendben van:

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
}
Most már tudod, hogy a lecke eleji kód miért nem fordítható le: a Java 7-ben egy helyi osztály csak a finalmetódusparaméterekhez és finala helyi változókhoz fér hozzá. A Java 8-ban a helyi osztályok viselkedése megváltozott. A nyelv ezen verziójában a helyi osztály nem csak a helyi változókhoz és paraméterekhez fér hozzá final, hanem azokhoz is, amelyek effective-final. Effective-finalegy olyan változó, amelynek értéke nem változott az inicializálás óta. Például a Java 8-ban könnyen megjeleníthetjük a usCountryCodeváltozót a konzolon, még akkor is, ha nem final. A lényeg az, hogy értéke ne változzon. A következő példában minden úgy működik, ahogy kell:

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
}
De ha az inicializálás után azonnal megváltoztatjuk a változó értékét, akkor a kód nem fordul le.

public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printUsCountryCode() {

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

    }

   // ...number validation code
}
Nem csoda, hogy a helyi osztály a belső osztály fogalmának alfaja! Közös jellemzőik is vannak. Egy helyi osztály hozzáfér a külső osztály összes (még privát) mezőjéhez és metódusához: statikus és nem statikus. Például adjunk hozzá egy statikus String phoneNumberRegexmezőt az érvényesítő osztályunkhoz:

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Az érvényesítés ezzel a statikus változóval történik. A metódus ellenőrzi, hogy az átadott karakterlánc tartalmaz-e olyan karaktereket, amelyek nem egyeznek a " [^0-9]" reguláris kifejezéssel (vagyis minden olyan karaktert, amely nem 0 és 9 közötti számjegy). Ezt a változót könnyen elérhetjük a helyi osztályból PhoneNumber. Például írjon egy gettert:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
A helyi osztályok hasonlóak a belső osztályokhoz, mivel nem definiálhatnak vagy deklarálhatnak statikus tagokat. A statikus metódusok helyi osztályai csak a befoglaló osztály statikus tagjaira hivatkozhatnak. Például, ha a befoglaló osztály egyik változóját (mezőjét) nem statikusként határozza meg, akkor a Java fordító hibát generál: "A nem statikus változóra nem lehet statikus környezetből hivatkozni." A helyi osztályok nem statikusak, mert hozzáférnek a befoglaló blokkban lévő példánytagokhoz. Ennek eredményeként a legtöbb statikus deklarációt nem tartalmazhatják. Nem deklarálhat interfészt egy blokkon belül: az interfészek természetüknél fogva statikusak. Ez a kód nem fordítja le:

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
   }
}
De ha egy interfész egy külső osztályon belül van deklarálva, az PhoneNumberosztály megvalósíthatja:

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
   }
}
A statikus inicializálók (inicializálási blokkok) vagy interfészek nem deklarálhatók a helyi osztályokban. De a helyi osztályoknak lehetnek statikus tagjai, feltéve, hogy állandó változók ( static final). És most már tudtok a helyi osztályokról, emberek! Amint láthatja, sok különbség van a szokásos belső osztályokhoz képest. Még a nyelv egyes verzióinak jellemzőibe is bele kellett ásnunk, hogy megértsük, hogyan működnek :) A következő leckében az anonim belső osztályokról lesz szó – a beágyazott osztályok utolsó csoportjáról. Sok sikert a tanuláshoz! :)
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION