JavaRush /Java-Blog /Random-DE /Innere Klassen in der lokalen Methode

Innere Klassen in der lokalen Methode

Veröffentlicht in der Gruppe Random-DE
Hallo! Lassen Sie uns über eine andere Art verschachtelter Klasse sprechen. Nämlich über lokale Klassen (Methode lokaler innerer Klassen). Das Erste, woran Sie sich vor dem Lernen erinnern müssen, ist ihr Platz in der Struktur verschachtelter Klassen. Innere Klassen in der lokalen Methode – 2Anhand unseres Diagramms können wir verstehen, dass lokale Klassen eine Unterart interner Klassen sind, über die wir in einem der vorherigen Materialien ausführlich gesprochen haben . Lokale Klassen weisen jedoch eine Reihe wichtiger Merkmale und Unterschiede zu internen Klassen auf. Der Schlüssel liegt in ihrer Deklaration: Eine lokale Klasse wird nur in einem Codeblock deklariert. Am häufigsten - innerhalb einer Methode einer externen Klasse. Es könnte zum Beispiel so aussehen:
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 валидации номера
   }
}
WICHTIG!Dieser Code lässt sich beim Einfügen in IDEA nicht kompilieren, wenn Sie Java 7 installiert haben. Über die Gründe dafür sprechen wir am Ende der Vorlesung. Kurz gesagt: Die Arbeit lokaler Klassen hängt stark von der Sprachversion ab. Wenn dieser Code nicht für Sie kompiliert werden kann, können Sie entweder die Sprachversion in IDEA auf Java 8 umstellen oder finaldem Methodenparameter ein Wort hinzufügen, sodass es wie folgt aussieht: validatePhoneNumber(final String number). Danach wird alles funktionieren. Dies ist ein kleines Programm – ein Telefonnummernvalidator. Seine Methode validatePhoneNumber()verwendet eine Zeichenfolge als Eingabe und bestimmt, ob es sich um eine Telefonnummer handelt. Und innerhalb dieser Methode haben wir unsere lokale Klasse deklariert PhoneNumber. Vielleicht haben Sie eine logische Frage: Warum? Warum eine Klasse innerhalb einer Methode deklarieren? Warum nicht eine reguläre innere Klasse verwenden? Tatsächlich könnte man dies tun: die Klasse PhoneNumberintern machen. Außerdem hängt die endgültige Entscheidung von der Struktur und dem Zweck Ihres Programms ab. Erinnern wir uns an unser Beispiel aus der Vorlesung über innere Klassen:
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("Gehen!");
   }

   public class HandleBar {

       public void right() {
           System.out.println(„Lenkrad nach rechts!“);
       }

       public void left() {

           System.out.println(„Lenkrad nach links!“);
       }
   }
}
Darin haben wir HandleBar(den Lenker) eine interne Klasse des Fahrrads geschaffen. Was ist der Unterschied? Zunächst einmal bei der Verwendung der Klasse. Die Klasse HandleBaraus dem zweiten Beispiel ist eine komplexere Entität als PhoneNumberaus dem ersten. Erstens HandleBarhat y öffentliche Methoden rightund left(sind keine Setter und Getter). Zweitens können wir nicht im Voraus vorhersagen, wo Bicyclewir es und seine externe Klasse benötigen – das können Dutzende verschiedener Orte und Methoden sein, sogar innerhalb desselben Programms. Aber mit einer Klasse PhoneNumberist alles viel einfacher. Unser Programm ist sehr einfach. Es hat nur eine Funktion – zu prüfen, ob es sich bei der Nummer um eine Telefonnummer handelt. In den meisten Fällen PhoneNumberValidatorhandelt es sich bei unserem Programm nicht einmal um ein eigenständiges Programm, sondern lediglich um einen Teil der Autorisierungslogik für das Hauptprogramm. Beispielsweise werden Sie auf verschiedenen Websites bei der Registrierung häufig aufgefordert, eine Telefonnummer einzugeben. Und wenn Sie statt Nummern irgendeinen Unsinn eingeben, zeigt die Site eine Fehlermeldung an: „Dies ist keine Telefonnummer!“ Für den Betrieb einer solchen Site (oder vielmehr des Benutzerautorisierungsmechanismus) können ihre Entwickler ein Analogon von uns in den Code einbinden PhoneNumberValidator. Mit anderen Worten: Wir haben eine externe Klasse mit einer Methode, die an einer Stelle im Programm und nirgendwo anders verwendet wird. Und wenn ja, dann wird sich daran nichts ändern: Eine Methode erledigt ihren Zweck – das ist alles. Da in diesem Fall die gesamte Arbeitslogik in einer Methode zusammengefasst ist, ist es viel bequemer und korrekter, dort eine zusätzliche Klasse zu kapseln. Es gibt außer Getter und Setter keine eigenen Methoden. Wir benötigen im Wesentlichen nur die Konstruktordaten davon. Es wird nicht in anderen Methoden verwendet. Daher gibt es keinen Grund, Informationen darüber über die einzelne Methode hinaus zu erweitern, in der es verwendet wird. Wir haben ein Beispiel für die Deklaration einer lokalen Klasse in einer Methode gegeben, aber das ist nicht die einzige Möglichkeit. Es kann einfach in einem Codeblock deklariert werden:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {


       //...Code валидации номера
   }
}
Oder sogar in einer Schleife 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;
               }
           }

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

       //...Code валидации номера
   }
}
Aber solche Fälle sind äußerst selten. In den meisten Fällen erfolgt die Deklaration weiterhin innerhalb der Methode. Also haben wir uns mit der Ankündigung befasst und auch über „Philosophie“ gesprochen :) Welche weiteren Merkmale und Unterschiede haben lokale Klassen von internen Klassen? Ein lokales Klassenobjekt kann nicht außerhalb der Methode oder des Blocks erstellt werden, in der es deklariert ist. Stellen Sie sich vor, wir benötigen eine Methode generatePhoneNumber(), die eine zufällige Telefonnummer generiert und eine zurückgibt PhoneNumber. Wir werden in der aktuellen Situation nicht in der Lage sein, eine solche Methode in unserer Validator-Klasse zu erstellen:
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() {

   }

}
Ein weiteres wichtiges Merkmal lokaler Klassen ist die Möglichkeit, auf lokale Variablen und Methodenparameter zuzugreifen. Falls Sie es vergessen haben: „local“ ist eine innerhalb einer Methode deklarierte Variable. Das heißt, wenn wir für einige unserer Zwecke eine lokale Variable String russianCountryCodeinnerhalb einer Methode erstellen validatePhoneNumber(), können wir über die lokale Klasse darauf zugreifen PhoneNumber. Allerdings gibt es hier viele Feinheiten, die von der im Programm verwendeten Sprachversion abhängen. Zu Beginn der Vorlesung haben wir darauf hingewiesen, dass der Code in einem der Beispiele möglicherweise nicht in Java 7 kompiliert wird. Erinnern Sie sich? Schauen wir uns nun die Gründe dafür an :) In Java 7 kann eine lokale Klasse nur dann auf eine lokale Variable oder einen Methodenparameter zugreifen, wenn diese in der Methode wie folgt deklariert sind final:
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

       //ошибка! параметр метода должен быть объявлен Wie final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           //ошибка! локальная переменная должна быть объявлена Wie final!
           System.out.println(russianCountryCode);
       }

   }

   //...Code валидации номера
}
Hier hat der Compiler zwei Fehler ausgegeben. Aber hier ist alles in Ordnung:
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 валидации номера
}
Jetzt kennen Sie den Grund, warum der Code zu Beginn der Vorlesung nicht kompiliert wurde: Eine lokale Klasse in Java 7 hat nur Zugriff auf final-method-Parameter und final-local-Variablen. In Java 8 hat sich das Verhalten lokaler Klassen geändert. In dieser Version der Sprache hat die lokale Klasse nicht nur Zugriff auf finallokale Variablen und Parameter, sondern auch auf effective-final. Effective-finalist eine Variable, deren Wert sich seit der Initialisierung nicht geändert hat. In Java 8 können wir beispielsweise problemlos eine Variable auf der Konsole anzeigen russianCountryCode, auch wenn dies nicht der Fall ist final. Die Hauptsache ist, dass es seine Bedeutung nicht ändert. In diesem Beispiel funktioniert alles wie es soll:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

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

    }

   //...Code валидации номера
}
Wenn wir jedoch den Wert der Variablen unmittelbar nach der Initialisierung ändern, wird der Code nicht kompiliert.
public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printRussianCountryCode() {

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

    }

   //...Code валидации номера
}
Aber nicht umsonst ist eine lokale Klasse ein Untertyp einer internen Klasse! Sie haben auch Gemeinsamkeiten. Eine lokale Klasse hat Zugriff auf alle (auch privaten) Felder und Methoden der äußeren Klasse: sowohl statische als auch nicht statische. Fügen wir beispielsweise ein statisches Feld zu unserer Validator-Klasse hinzu String phoneNumberRegex:
public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
Die Validierung wird mithilfe dieser statischen Variablen durchgeführt. Die Methode prüft, ob die ihr übergebene Zeichenfolge Zeichen enthält, die nicht mit dem regulären Ausdruck „ [^0-9]“ übereinstimmen (d. h. das Zeichen ist keine Zahl von 0 bis 9). Wir können von der lokalen Klasse aus leicht auf diese Variable zugreifen PhoneNumber. Schreiben Sie zum Beispiel einen Getter:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
Lokale Klassen ähneln inneren Klassen, da sie keine statischen Mitglieder definieren oder deklarieren können. Lokale Klassen in statischen Methoden können nur auf statische Mitglieder der einschließenden Klasse verweisen. Wenn Sie beispielsweise eine Variable (ein Feld) der umschließenden Klasse nicht als statisch definieren, generiert der Java-Compiler einen Fehler: „Eine nicht statische Variable kann nicht aus einem statischen Kontext referenziert werden.“ Lokale Klassen sind nicht statisch, da sie Zugriff auf die Instanzmitglieder des enthaltenden Blocks haben. Daher können sie die meisten Arten statischer Deklarationen nicht enthalten. Sie können eine Schnittstelle nicht innerhalb eines Blocks deklarieren; Schnittstellen sind statischer Natur. Dieser Code lässt sich nicht kompilieren:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...Code валидации номера
   }
}
Wenn jedoch eine Schnittstelle innerhalb einer äußeren Klasse deklariert wird, PhoneNumberkann die Klasse sie implementieren:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...Code валидации номера
   }
}
Lokale Klassen können keine statischen Initialisierer (Initialisierungsblöcke) oder Schnittstellen deklarieren. Aber lokale Klassen können statische Mitglieder haben, vorausgesetzt, es handelt sich um konstante Variablen ( static final). Das sind sie, lokale Klassen! Wie Sie sehen, weisen sie viele Unterschiede zu internen Klassen auf. Wir mussten sogar in die Funktionen der Sprachversion eintauchen, um zu verstehen, wie sie funktionieren :) In der nächsten Vorlesung werden wir über anonyme innere Klassen sprechen – die letzte Gruppe verschachtelter Klassen. Viel Erfolg beim Studium! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION