• Home
  • LLMs
  • Python
  • Docker
  • Kubernetes
  • Java
  • Maven
  • All
  • About
Java | Classes internes (inner classes)
  1. Classes internes (inner classes)
  2. Classes internes statiques (static inner classes | static nested classes)
  3. Classes internes locales (local inner classes)
  4. Classes internes anonymes (anonymous inner classes)
    1. Définir une classe anonyme à partir d'une autre classe
    2. Définir une classe anonyme à partir d'une classe abstraite
    3. Définir une classe anonyme à partir d'une interface

  1. Classes internes (inner classes)
    Une classe interne est une classe définie à l'intérieur d'une autre classe (appelée classe externe).
    class MyOuterClass { // outer class
        class MyInnerClass { // inner class
        }
    }
    Notes :
    • Le compilateur va créer deux fichiers séparés :
      • Un fichier pour la classe externe (MyOuterClass.class),
      • et un autre fichier pour la classe interne (MyOuterClass$MyInnerClass.class).

    • La classe interne ne peut pas déclarer des attributs statiques ou des méthodes statiques.
      class MyOuterClass { // outer class
          class MyInnerClass { // inner class
              private static int var = 0; // compiler error: The field var cannot be declared static; static fields can only be declared in static or top level types
      
              public static int getVar() { // compiler error: The method getVar cannot be declared static; static methods can only be declared in a static or top level type
                  return var;
              }
      
              public void setVar(int var) { // OK
                  this.var = var;
              }
          }
      }
      La classe interne est un membre non-statique de la classe externe, donc le seul moyen d'y accéder est à travers une instance de la classe externe. Ça ne fait pas de sens, donc, de déclarer des membres (attributs et méthodes) statiques pour la classe interne.

    • Pour déclarer (à l'intérieur de la classe externe) une variable de type de la classe interne, ou créer (à l'intérieur de la classe externe) une instance de la classe interne, il faut utiliser le nom de la classe interne comme on le fait pour les classes ordinaires.
      class MyOuterClass { // outer class
          MyInnerClass myInnerClass_1 = new MyInnerClass(); // déclarer la variable de type de la classe interne + créer une instance de la classe interne
      
          void CreateInstanceOfMyInnerClass() {
              MyInnerClass myInnerClass_2; // déclarer la variable de type de la classe interne
              myInnerClass_2 = new MyInnerClass(); // créer une instance de la classe interne
          }
      
          class MyInnerClass { // inner class
          }
      }
      Il n'est pas possible d'instancier directement la classe interne dans une méthode statique de la classe externe. Le compilateur va afficher une erreur :
      class MyOuterClass { // outer class
          static void CreateInstanceOfMyInnerClass() {
              MyInnerClass myInnerClass; // OK
      
              myInnerClass = new MyInnerClass(); /* compiler error:
                                                      No enclosing instance of type MyOuterClass is accessible.
                                                      Must qualify the allocation with an enclosing instance of type MyOuterClass (e.g. x.new A() where x is an instance of MyOuterClass).
                                                  */
          }
      
          class MyInnerClass { // inner class
          }
      }
      Comme indiquer dans le message d'erreur du compilateur, il faut passer par une instance de la classe externe pour pouvoir instancier la classe interne :
      class MyOuterClass { // outer class
          static void CreateInstanceOfMyInnerClass() {
              MyInnerClass myInnerClass; // OK
      
              MyOuterClass myOuterClass = new MyOuterClass();
              myInnerClass = myOuterClass.new MyInnerClass(); // OK
      
              myInnerClass = new MyOuterClass().new MyInnerClass(); // OK
          }
      
          class MyInnerClass { // inner class
          }
      }
    • Il n'est pas possible de déclarer directement une variable de la classe interne ou l'instancier (de l'extérieur de la classe externe) ; il faut toujours utiliser la classe externe pour référencer la classe interne, sinon le compilateur affichera une erreur :
      class MyOuterClass { // outer class
          class MyInnerClass { // inner class
          }
      }
      
      public class MyTest1 {
          public static void main(String[] args) {
              MyInnerClass myInnerClass; // compiler error: MyInnerClass cannot be resolved to a type
      
              myInnerClass = new MyInnerClass(); // compiler error: MyInnerClass cannot be resolved to a type
          }
      }
    • Pour déclarer une variable qui référence la classe interne (de l'extérieur de la classe externe), il faut utiliser le nom de la classe externe concaténé (par un point) avec le nom de la classe interne.
      class MyOuterClass { // outer class
          class MyInnerClass { // inner class
          }
      }
      
      public class MyTest1 {
          public static void main(String[] args) {
              MyOuterClass.MyInnerClass myInnerClass; // création de la variable de type de la classe interne
          }
      }
    • Pour créer une instance de la classe interne (de l'extérieur de la classe externe), il faut passer par une instance de la classe externe.
      class MyOuterClass { // outer class
          class MyInnerClass { // inner class
          }
      }
      
      public class MyTest1 {
          public static void main(String[] args) {
              MyOuterClass.MyInnerClass myInnerClass;
      
              MyOuterClass myOuterClass = new MyOuterClass();
              myInnerClass = myOuterClass.new MyInnerClass();
      
              myInnerClass = new MyOuterClass().new MyInnerClass(); // instance anonyme de la clase externe
          }
      }
    • La déclaration de la classe interne peut utiliser les modificateurs d'accès public, protected, private, ou eventuellement n'avoir aucun de ces modificateurs d'accès (dans ce cas elle aura l'accès default).
      class MyOuterClass { // outer class
          /*[public|protected|private]*/ class MyInnerClass { // inner class
          }
      }
      Les règles de visibilités s'appliquent lorsque la classe interne est déclarée protected et private.
      Par exemple, il n'est pas possible de référencer (de l'extérieur de la classe externe) la classe interne s'elle est déclarée privée à la classe externe.
      class MyOuterClass { // outer class
          private Integer var = 0;
      
          public Integer getVar() {
              return var;
          }
      
          public MyInnerClass initMyInnerClass() {
              MyInnerClass myInnerClass = new MyInnerClass();
              return myInnerClass;
          }
      
          public void setNextNumberFromOuterClass() {
              MyInnerClass myInnerClass = new MyInnerClass(); // OK
              myInnerClass.setNextNumberFromInnerClass();
          }
      
          private class MyInnerClass { // inner class
              public void setNextNumberFromInnerClass() {
                  ++var;
              }
          }
      }
      
      public class MyTest1 {
          public static void main(String[] args) {
              MyOuterClass myOuterClass = new MyOuterClass();
      
              myOuterClass.setNextNumberFromOuterClass();
              System.out.println(myOuterClass.getVar());
      
              myOuterClass.initMyInnerClass().setNextNumberFromInnerClass(); // compiler error: The type MyOuterClass.MyInnerClass is not visible
              System.out.println(myOuterClass.getVar());
      
              MyOuterClass.MyInnerClass myInnerClass; // compiler error: The type MyOuterClass.MyInnerClass is not visible
              myInnerClass = myOuterClass.new MyInnerClass(); // compiler error: The type MyOuterClass.MyInnerClass is not visible
              myInnerClass.setNextNumberFromInnerClass(); // compiler error: The type MyOuterClass.MyInnerClass is not visible
              System.out.println(myOuterClass.getVar());
          }
      }
    • La déclaration de la classe interne peut utiliser les modificateurs d'accès abstract et final (l'un ou l'autre, mais pas les deux au même temps !).
      class MyOuterClass { // outer class
          /*[abstract|final]*/ class MyInnerClass { // inner class
          }
      }
    • Il est possible d'accéder aux attributs et méthodes de la classe externe à partir de la classe interne, de la même manière que cela est possible à partir des méthodes de la classe externe.

      Il faut cependant noter que si la classe interne déclare des attributs (ou des méthodes) avec les mêmes noms que ceux de la classe externe, alors dans ce cas il faut utiliser MyOuterClass.this pour référencer les attributs (et les méthodes) de la classe externe à partir de la classe interne. Au cas contraire, les attributs et les méthodes de la classe interne seront utilisées par la JVM.

      Attention le this référence toujours l'instance du code en cours d'exécution (peu importe que ce soit le code de la classe interne ou externe).
      class MyOuterClass { // outer class
          private Integer var = 0;
          private Integer step = 1;
      
          public Integer getVar() {
              return var; // ~ return this.var;
          }
      
          public class MyInnerClass { // inner class
              private Integer step = 2;
      
              public void setNextNumber() {
                  var = var + step;
                  // ~ MyOuterClass.this.var = MyOuterClass.this.var + this.step;
                  // ~ MyOuterClass.this.var = MyOuterClass.this.var + MyInnerClass.this.step;
              }
          }
      }
      
      public class MyTest1 {
          public static void main(String[] args) {
              MyOuterClass myOuterClass = new MyOuterClass();
      
              MyOuterClass.MyInnerClass myInnerClass = myOuterClass.new MyInnerClass();
              myInnerClass.setNextNumber();
              System.out.println(myOuterClass.getVar());
          }
      }
  2. Classes internes statiques (static inner classes | static nested classes)
    Une classe interne statique est une classe définie à l'intérieur d'une autre classe (classe externe).

    La classe interne statique est un membre statique de la classe externe.
    Ce qui veut dire qu'elle est accessible sans avoir besoin de créer une instance de la classe externe.

    Les règles que nous avons vues ci-dessus pour les classes internes non-statiques s'appliquent aussi sur les classes internes statiques.
    Voici quelques particularités des classes internes statiques :
    • La classe interne statique peut déclarer des membres (attributs et méthodes) statiques et non-statiques.
      class MyOuterClass { // outer class
          public static class MyNestedClass { // inner class
              static Integer var = 0;
              Integer step = 0;
      
              void doSomeThing() {
              }
      
              static void doSomeThingElse() {
              }
          }
      }
    • Il n'est pas nécessaire d'instancier ni la classe interne ni la classe externe pour accéder aux membres (attributs et méthodes) statiques de la classe interne statique.
      class MyOuterClass { // outer class
          public static class MyNestedClass { // inner class
              static Integer var = 0;
              Integer step = 1;
      
              void doSomeThing() {
              }
      
              static void doSomeThingElse() {
              }
          }
      }
      
      public class MyTest2 {
          public static void main(String[] args) {
              MyOuterClass.MyNestedClass.var = 1;
      
              MyOuterClass.MyNestedClass.doSomeThingElse();
          }
      }
    • Il n'est pas nécessaire d'instancier la classe externe pour créer une instance de la classe interne statique (de l'extérieur de la classe externe).
      class MyOuterClass { // outer class
          public static class MyNestedClass { // inner class
          }
      }
      
      public class MyTest2 {
          public static void main(String[] args) {
              MyOuterClass.MyNestedClass myNestedClass = new MyOuterClass.MyNestedClass();
          }
      }
    • La classe interne statique ne peut pas accéder aux membres (attributs et méthodes) non-statiques de la classe externe.
      class MyOuterClass { // outer class
          static Integer var = 0;
          Integer step = 1;
      
          static void initVar() {
              var = 1;
          }
      
          void initStep() {
              step = 1;
          }
      
          public static class MyNestedClass { // inner class
              void doSomeThing() {
                  MyOuterClass.var = 1; // OK
                  MyOuterClass.this.step = 1; // compiler error: No enclosing instance of the type MyOuterClass is accessible in scope
      
                  MyOuterClass.initVar(); // OK
                  MyOuterClass.initStep(); // compiler error: Cannot make a static reference to the non-static method initStep() from the type MyOuterClass
              }
      
              static void doSomeThingElse() {
                  MyOuterClass.var = 1; // OK
                  MyOuterClass.this.step = 1; // compiler error: No enclosing instance of the type MyOuterClass is accessible in scope
      
                  MyOuterClass.initVar(); // OK
                  MyOuterClass.initStep(); // compiler error: Cannot make a static reference to the non-static method initStep() from the type MyOuterClass
              }
          }
      }
  3. Classes internes locales (local inner classes)
    Une classe interne locale est une classe définie à l'intérieur d'une méthode.
    public class MyTest3 {
        public static void main(String[] args) {
            class MyLocalInnerClass { // local inner class
            }
    
            MyLocalInnerClass myLocalInnerClass = new MyLocalInnerClass();
        }
    }
    Notes :
    • Le compilateur va créer des fichiers séparés pour chaque classe interne locale.
      public class MyTest3 {
          public static void main(String[] args) {
              class MyLocalInnerClass1 { // local inner class
              }
      
              class MyLocalInnerClass2 { // local inner class
              }
          }
      }
      Dans cet exemple le compilateur va créer les fichiers ".class" suivants :
      • MyTest3.class
      • MyTest3$1MyLocalInnerClass1.class
      • MyTest3$1MyLocalInnerClass2.class

    • La déclaration de la classe locale peut utiliser seulement les modificateurs d'accès abstract ou final (l'un ou l'autre, mais pas les deux au même temps !). Le compilateur va afficher une erreur si la déclaration de la classe contient, par exemple, le mot clé public :
      public class MyTest3 {
          public void doSomeThing() {
              public class MyLocalInnerClass { // compiler error: Illegal modifier for the local class MyLocalInnerClass; only abstract or final is permitted
              }
          }
      }
    • La classe locale n'est visible que pour le code de la méthode où elle est définie ; elle peut être référée et instanciée uniquement à l'intérieur de ce code. Attention, seulement le code de la méthode qui est saisie après la définition de la classe locale peut référer et instancier la classe locale. Le compilateur va afficher une erreur si ce n'est pas le cas :
      public class MyTest3 {
          public void doSomeThing() {
              MyLocalInnerClass myLocalInnerClass = new MyLocalInnerClass(); // compiler error: MyLocalInnerClass cannot be resolved to a type
      
              class MyLocalInnerClass { // local inner class
              }
          }
      }
    • La classe locale peut déclarer seulement des membres (attributs et méthodes) non-statiques. Le compilateur va afficher une erreur si ce n'est pas le cas :
      public class MyTest3 {
          public void doSomeThing() {
              class MyLocalInnerClass { // local inner class
                  private static Integer var = 0; // compiler error: The field var cannot be declared static; static fields can only be declared in static or top level types
      
                  public static void foo() { // compiler error: The method foo cannot be declared static; static methods can only be declared in a static or top level type
                  }
              }
          }
      }
    • Les classes locales définies dans des méthodes statiques ne peuvent accéder qu'aux membres statiques des classes externes. Le compilateur va afficher une erreur si ce n'est pas le cas :
      public class MyTest3 {
          private static Integer var1 = 2;
          private Integer var2 = 2;
      
          public static void foo1() {
          }
      
          public void bar1() {
          }
      
          public static void doSomeThing() {
              class MyLocalInnerClass { // local inner class
                  public void foo() {
                      Integer localVar1 = var1; // OK
                      Integer localVar2 = var2; // compiler error: Cannot make a static reference to the non-static field var2
      
                      foo1(); // OK
                      bar1(); // compiler error: Cannot make a static reference to the non-static method bar1() from the type MyTest3
                  }
              }
          }
      }
      Par contre, les classes locales définies dans des méthodes non-statiques peuvent accéder aux membres statiques et non-statiques des classes externes.
      public class MyTest3 {
          private static Integer var1 = 2;
          private Integer var2 = 2;
      
          public static void foo1() {
          }
      
          public void bar1() {
          }
      
          public void doSomeThing() {
              class MyLocalInnerClass { // local inner class
                  public void foo() {
                      Integer localVar1 = var1; // OK
                      Integer localVar2 = var2; // OK
      
                      foo1(); // OK
                      bar1(); // OK
                  }
              }
          }
      }
    • La classe locale peut accéder uniquement aux variables de la méthode qui sont déclarées avec le mot clé final. Le compilateur va afficher une erreur si ce n'est pas le cas :
      public class MyTest3 {
          public void doSomeThing() {
              final Integer var1 = 2;
              Integer var2 = 2;
      
              class MyLocalInnerClass { // local inner class
                  public void foo() {
                      Integer localVar1 = var1; // OK
                      Integer localVar2 = var2; // compiler error: Cannot refer to a non-final variable var2 inside an inner class defined in a different method
                  }
              }
          }
      }
  4. Classes internes anonymes (anonymous inner classes)
    Les classes internes anonymes sont des classes définies à partir de d'autres classes ou interfaces.

    Les classes anonymes n'ont pas de noms.

    La classe interne anonyme est utilisée pour re-définir une classe ou implémenter une interface lors d'une opération d'instanciation.

    La définition d'une classe interne anonyme est possible dans tout le code de la classe externe ; là où l'instanciation d'une classe est possible.

    1. Définir une classe anonyme à partir d'une autre classe
      public class MyTest4 {
          Object object = new Object() { // anonymous inner class
              @Override
              public String toString() {
                  return "anonymous inner class:toString";
              }
          };
      }
      Dans cet exemple, une nouvelle classe anonyme est créée à partir de la classe Object :
      la nouvelle classe anonyme hérite implicitement de la classe Object et peut ainsi redéfinir ses méthodes et peut aussi définir de nouvelles méthodes mais ces nouvelles méthodes seront visibles uniquement dans le code de la classe anonyme.

      La raison de cette restriction est due au fait que l'instance créée à partir de la classe anonyme est référée par une variable dont le type est celui de la classe Object et donc le compilateur va se plaindre si vous essayer d'appeler les nouvelles méthodes définies dans la classe anonyme :
      public class MyTest4 {
          public static void main(String[] args) {
              Object object = new Object() { // anonymous inner class
                  @Override
                  public String toString() {
                      return toStringAnonymous(); // OK
                  }
      
                  public String toStringAnonymous() {
                      return "anonymes inner classes";
                  }
              };
      
              object.toString();
              object.toStringAnonymous(); // compiler error: The method toStringAnonymous() is undefined for the type Object
          }
      }
      Il est possible de définir une classe anonyme comme argument d'une méthode :
      public class MyTest4 {
          public static void main(String[] args) {
              foo(
                  new Object() { // anonymous inner class
                          @Override
                          public String toString() {
                              return "anonymes inner classes";
                          }
                  }
              );
          }
      
          public static void foo(Object object) {
              object.toString();
          }
      }
      Notes :
      Le compilateur va créer des fichiers séparés pour chaque classe interne anonyme.
      public class MyTest4 {
          Object object1 = new Object() { // anonymous inner class
          };
      
          Object object2 = new Object() { // anonymous inner class
          };
      }
      Dans cet exemple le compilateur va créer les fichiers ".class" suivants :
      • MyTest4.class
      • MyTest4$1.class
      • MyTest4$2.class
    2. Définir une classe anonyme à partir d'une classe abstraite
      La classe anonyme doit implémenter toutes les méthodes abstraites de la classe abstraite.

      abstract class MyAbstractClass {
          abstract void foo(); // the compiler will throw an error if not implemented
      
          abstract void bar(); // the compiler will throw an error if not implemented
      
          void doSomeThing() { // OK
          }
      }
      
      public class MyTest4 {
          MyAbstractClass myAbstractClass = new MyAbstractClass() { // compiler error: The type new MyAbstractClass(){} must implement the inherited abstract method MyAbstractClass.bar()
              @Override
              public void foo() {
              }
          };
      }
    3. Définir une classe anonyme à partir d'une interface
      La classe anonyme peut implémenter une seule interface.
      Comme pour les classes abstraites, la classe anonyme doit implémenter toutes les méthodes de l'interface.

      interface MyInterface {
          void foo();
      }
      
      public class MyTest4 {
          public static void main(String[] args) {
              MyInterface myInterface = new MyInterface() { // anonymous inner class
                  @Override
                  public void foo() {
                  }
              };
      
              myInterface.foo();
          }
      }
© 2025  mtitek