Classes imbriquées en Java

1. Introduction

Ce didacticiel est une introduction rapide et précise aux classes imbriquées dans le langage Java.

En termes simples, Java nous permet de définir des classes à l'intérieur d'autres classes. Les classes imbriquées nous permettent de regrouper logiquement les classes qui ne sont utilisées qu'à un seul endroit, d'écrire du code plus lisible et maintenable et d'augmenter l'encapsulation.

Avant de commencer, jetons un œil aux différents types de classes imbriquées disponibles dans le langage:

  • Classes imbriquées statiques
  • Classes imbriquées non statiques
  • Cours locaux
  • Cours anonymes

Dans les sections suivantes, nous allons discuter de chacun de ces éléments en détail.

2. Classes imbriquées statiques

Voici quelques points à retenir sur les classes imbriquées statiques:

  • Comme pour les membres statiques, ceux-ci appartiennent à leur classe englobante et non à une instance de la classe
  • Ils peuvent avoir tous les types de modificateurs d'accès dans leur déclaration
  • Ils n'ont accès qu'aux membres statiques de la classe englobante
  • Ils peuvent définir des membres statiques et non statiques

Voyons comment nous pouvons déclarer une classe imbriquée statique:

public class Enclosing { private static int x = 1; public static class StaticNested { private void run() { // method implementation } } @Test public void test() { Enclosing.StaticNested nested = new Enclosing.StaticNested(); nested.run(); } }

3. Classes imbriquées non statiques

Ensuite, voici quelques points rapides à retenir sur les classes imbriquées non statiques:

  • Ils sont également appelés classes internes
  • Ils peuvent avoir tous les types de modificateurs d'accès dans leur déclaration
  • Tout comme les variables et les méthodes d'instance, les classes internes sont associées à une instance de la classe englobante
  • Ils ont accès à tous les membres de la classe englobante, qu'ils soient statiques ou non statiques
  • Ils ne peuvent définir que des membres non statiques

Voici comment nous pouvons déclarer une classe interne:

public class Outer { public class Inner { // ... } }

Si nous déclarons une classe imbriquée avec un modificateur static , alors c'est un membre statique. Sinon, c'est une classe interne. Même si syntaxiquement la différence est juste un seul mot-clé (c'est-à-dire, statique ), sémantiquement il y a une énorme différence entre ces types de classes imbriquées. Les instances de classe interne sont liées à celles de classe englobantes et ont donc accès à leurs membres. Nous devons être conscients de ce problème lorsque nous choisissons de faire d'une classe imbriquée une classe interne.

Pour instancier une classe interne, nous devons d'abord instancier sa classe englobante.

Voyons comment nous pouvons faire cela:

Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();

Dans les sous-sections suivantes, nous allons montrer quelques types spéciaux de classes internes.

3.1. Classes locales

Les classes locales sont un type spécial de classes internes - dans lesquelles la classe est définie à l'intérieur d'une méthode ou d'un bloc de portée.

Voyons quelques points à retenir sur ce type de cours:

  • Ils ne peuvent pas avoir de modificateurs d'accès dans leur déclaration
  • Ils ont accès aux membres statiques et non statiques dans le contexte englobant
  • Ils ne peuvent définir que des membres d'instance

Voici un exemple rapide:

public class NewEnclosing { void run() { class Local { void run() { // method implementation } } Local local = new Local(); local.run(); } @Test public void test() { NewEnclosing newEnclosing = new NewEnclosing(); newEnclosing.run(); } }

3.2. Classes anonymes

Les classes anonymes peuvent être utilisées pour définir une implémentation d'une interface ou d'une classe abstraite sans avoir à créer une implémentation réutilisable.

Énumérons quelques points à retenir sur les classes anonymes:

  • Ils ne peuvent pas avoir de modificateurs d'accès dans leur déclaration
  • Ils ont accès aux membres statiques et non statiques dans le contexte englobant
  • Ils ne peuvent définir que des membres d'instance
  • Ce sont les seuls types de classes imbriquées qui ne peuvent pas définir de constructeurs ou étendre / implémenter d'autres classes ou interfaces

Pour définir une classe anonyme, définissons d'abord une classe abstraite simple:

abstract class SimpleAbstractClass { abstract void run(); }

Now let's see how we can define an anonymous class:

public class AnonymousInnerUnitTest { @Test public void whenRunAnonymousClass_thenCorrect() { SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() { void run() { // method implementation } }; simpleAbstractClass.run(); } }

For more details, we may find useful our tutorial on Anonymous Classes in Java.

4. Shadowing

The declaration of the members of an inner class shadow those of the enclosing class if they have the same name.

In this case, the this keyword refers to the instances of the nested class and the members of the outer class can be referred to using the name of the outer class.

Let's see a quick example:

public class NewOuter { int a = 1; static int b = 2; public class InnerClass { int a = 3; static final int b = 4; public void run() { System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("NewOuterTest.this.a = " + NewOuter.this.a); System.out.println("NewOuterTest.b = " + NewOuter.b); System.out.println("NewOuterTest.this.b = " + NewOuter.this.b); } } @Test public void test() { NewOuter outer = new NewOuter(); NewOuter.InnerClass inner = outer.new InnerClass(); inner.run(); } }

5. Serialization

To avoid a java.io.NotSerializableException while attempting to serialize a nested class, we should:

  • Declare the nested class as static
  • Make both the nested class and the enclosing class implement Serializable

6. Conclusion

Dans cet article, nous avons vu ce que sont les classes imbriquées et leurs différents types. Nous avons également examiné comment la visibilité des champs et les modificateurs d'accès diffèrent entre ces différents types.

Comme toujours, l'implémentation complète de ce tutoriel peut être trouvée sur GitHub.