CodeGym /Blog Java /Random-FR /Classes internes dans une méthode locale
Auteur
Oleksandr Miadelets
Head of Developers Team at CodeGym

Classes internes dans une méthode locale

Publié dans le groupe Random-FR
Salut! Parlons d'un autre type de classes imbriquées. Je parle de classes locales (méthode-classes internes locales). Avant de plonger, il faut d'abord rappeler leur place dans la structure des classes imbriquées. Classes internes dans une méthode locale - 2À partir de notre diagramme, nous pouvons voir que les classes locales sont une sous-espèce des classes internes, dont nous avons parlé en détail dans les documents précédents . Cependant, les classes locales ont un certain nombre de caractéristiques et de différences importantes par rapport aux classes internes ordinaires. L'essentiel est dans leur déclaration : une classe locale n'est déclarée que dans un bloc de code. Le plus souvent, cette déclaration se trouve à l'intérieur d'une méthode de la classe externe. Par exemple, cela pourrait ressembler à ceci :

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!Si Java 7 est installé, ce code ne sera pas compilé lorsqu'il sera collé dans IDEA. Nous en expliquerons les raisons à la fin de la leçon. En bref, le fonctionnement des classes locales dépend fortement de la version de la langue. Si ce code ne se compile pas pour vous, vous pouvez soit basculer la version linguistique dans IDEA vers Java 8, soit ajouter le mot finalau paramètre de méthode pour qu'il ressemble à ceci : validatePhoneNumber(final String number). Après cela, tout fonctionnera. Il s'agit d'un petit programme qui valide les numéros de téléphone. Sa validatePhoneNumber()méthode prend une chaîne en entrée et détermine s'il s'agit d'un numéro de téléphone. Et à l'intérieur de cette méthode, nous avons déclaré notre PhoneNumberclasse locale. Vous pourriez raisonnablement demander pourquoi. Pourquoi déclarerions-nous exactement une classe à l'intérieur d'une méthode ? Pourquoi ne pas utiliser une classe interne ordinaire ? Certes, nous aurions pu faire lePhoneNumberclasse une classe intérieure. Mais la solution finale dépend de la structure et de l'objectif de votre programme. Rappelons notre exemple d'une leçon sur les classes internes :

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!");
       }
   }
}
Dans celui-ci, nous avons créé HandleBarune classe intérieure du vélo. Quelle est la différence? Tout d'abord, la façon dont la classe est utilisée est différente. La HandleBarclasse du deuxième exemple est une entité plus complexe que la PhoneNumberclasse du premier exemple. Tout d'abord, HandleBara public rightet leftméthodes (ce ne sont pas des setters/getters). Deuxièmement, il est impossible de prédire à l'avance où nous pourrions en avoir besoin et sa Bicycleclasse extérieure. Il pourrait y avoir des dizaines d'endroits et de méthodes différentes, même dans un seul programme. Mais avec la PhoneNumberclasse, tout est beaucoup plus simple. Notre programme est très simple. Il n'a qu'un seul but : vérifier si un numéro est un numéro de téléphone valide. Dans la plupart des cas, notrePhoneNumberValidatorne sera même pas un programme autonome, mais plutôt une partie de la logique d'autorisation d'un programme plus vaste. Par exemple, divers sites Web demandent souvent un numéro de téléphone lorsque les utilisateurs s'inscrivent. Si vous entrez des absurdités au lieu de chiffres, le site Web signalera une erreur : "Ceci n'est pas un numéro de téléphone !" Les développeurs d'un tel site Web (ou plutôt, son mécanisme d'autorisation d'utilisateur) peuvent inclure quelque chose de similaire à notrePhoneNumberValidatordans leur code. En d'autres termes, nous avons une classe externe avec une méthode, qui sera utilisée à un endroit du programme et nulle part ailleurs. Et s'il est utilisé, rien ne changera : une méthode fait son travail — et c'est tout. Dans ce cas, comme toute la logique est regroupée dans une seule méthode, il sera beaucoup plus pratique et approprié d'y encapsuler une classe supplémentaire. Il n'a pas de méthodes propres à l'exception d'un getter et d'un setter. En fait, nous n'avons besoin que des données du constructeur. Il n'est pas impliqué dans d'autres méthodes. En conséquence, il n'y a aucune raison de prendre des informations à son sujet en dehors de la seule méthode où elle est utilisée. Nous avons également donné un exemple dans lequel une classe locale est déclarée dans une méthode, mais ce n'est pas la seule option. Il peut être déclaré simplement dans un bloc de code :

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Ou même dans la forboucle !

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
   }
}
Mais de tels cas sont extrêmement rares. Dans la plupart des cas, la déclaration se fera à l'intérieur de la méthode. Donc, nous avons compris les déclarations, et nous avons également parlé de la "philosophie" :) Quelles fonctionnalités et différences supplémentaires les classes locales ont-elles par rapport aux classes internes ? Un objet d'une classe locale ne peut pas être créé en dehors de la méthode ou du bloc dans lequel il est déclaré. Imaginez que nous ayons besoin d'une generatePhoneNumber()méthode qui générera un numéro de téléphone aléatoire et renverra un PhoneNumberobjet. Dans notre situation actuelle, nous ne pouvons pas créer une telle méthode dans notre classe validator :

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

   }

}
Une autre caractéristique importante des classes locales est la possibilité d'accéder aux variables locales et aux paramètres de méthode. Au cas où vous l'auriez oublié, une variable déclarée dans une méthode est appelée variable "locale". Autrement dit, si nous créons une String usCountryCodevariable locale dans la validatePhoneNumber()méthode pour une raison quelconque, nous pouvons y accéder à partir de la PhoneNumberclasse locale. Cependant, il existe de nombreuses subtilités qui dépendent de la version du langage utilisé dans le programme. Au début de la leçon, nous avons noté que le code de l'un des exemples pouvait ne pas être compilé en Java 7, vous vous souvenez ? Examinons maintenant les raisons de cela :) En Java 7, une classe locale ne peut accéder à une variable locale ou à un paramètre de méthode que s'ils sont déclarés comme finaldans la méthode :

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
}
Ici, le compilateur génère deux erreurs. Et tout est en ordre ici :

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
}
Vous savez maintenant pourquoi le code du début de la leçon ne se compilerait pas : en Java 7, une classe locale n'a accès qu'aux finalparamètres de méthode et finalaux variables locales. Dans Java 8, le comportement des classes locales a changé. Dans cette version du langage, une classe locale a accès non seulement aux finalvariables et paramètres locaux, mais aussi à ceux qui sont effective-final. Effective-finalest une variable dont la valeur n'a pas changé depuis l'initialisation. Par exemple, en Java 8, on peut facilement afficher la usCountryCodevariable sur la console, même si ce n'est pas final. L'important est que sa valeur ne change pas. Dans l'exemple suivant, tout fonctionne comme il se doit :

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
}
Mais si nous modifions la valeur de la variable immédiatement après l'initialisation, le code ne compilera pas.

public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printUsCountryCode() {

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

    }

   // ...number validation code
}
Pas étonnant qu'une classe locale soit une sous-espèce du concept de classe interne ! Ils ont aussi des caractéristiques communes. Une classe locale a accès à tous les champs et méthodes (même privés) de la classe externe : à la fois statiques et non statiques. Par exemple, ajoutons un String phoneNumberRegexchamp statique à notre classe de validateur :

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
La validation sera effectuée à l'aide de cette variable statique. La méthode vérifie si la chaîne transmise contient des caractères qui ne correspondent pas à l'expression régulière " [^0-9]" (c'est-à-dire tout caractère qui n'est pas un chiffre compris entre 0 et 9). Nous pouvons facilement accéder à cette variable depuis la PhoneNumberclasse locale. Par exemple, écrivez un getter :

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Les classes locales sont similaires aux classes internes, car elles ne peuvent pas définir ou déclarer de membres statiques. Les classes locales dans les méthodes statiques ne peuvent référencer que des membres statiques de la classe englobante. Par exemple, si vous ne définissez pas une variable (champ) de la classe englobante comme statique, le compilateur Java génère une erreur : "La variable non statique ne peut pas être référencée à partir d'un contexte statique." Les classes locales ne sont pas statiques, car elles ont accès aux membres d'instance dans le bloc englobant. Par conséquent, ils ne peuvent pas contenir la plupart des types de déclarations statiques. Vous ne pouvez pas déclarer une interface à l'intérieur d'un bloc : les interfaces sont par nature statiques. Ce code ne compile pas :

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
   }
}
Mais si une interface est déclarée dans une classe externe, la PhoneNumberclasse peut l'implémenter :

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
   }
}
Les initialiseurs statiques (blocs d'initialisation) ou les interfaces ne peuvent pas être déclarés dans les classes locales. Mais les classes locales peuvent avoir des membres statiques, à condition qu'il s'agisse de variables constantes ( static final). Et maintenant, vous connaissez les cours locaux, les amis ! Comme vous pouvez le voir, ils présentent de nombreuses différences par rapport aux classes internes ordinaires. Nous avons même dû nous plonger dans les fonctionnalités de versions spécifiques du langage afin de comprendre comment elles fonctionnent :) Dans la prochaine leçon, nous parlerons des classes internes anonymes - le dernier groupe de classes imbriquées. Bonne chance dans tes études! :)
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION