Pages

lundi 19 novembre 2012

Les classes imbriquées en Java (Nested class)

Une petite piqûre de rappel sur les classes imbriquées

Le langage Java permet de créer des classes à l’intérieur d’une autre classe. On parle de classes imbriquées (nested class)


class A {
   private class B{
   }
}

Au niveau de la terminologie les classes imbriquées sont divisées en deux catégories : static ou non static. On parle de classes statiques imbriquées (static nested class) et de classes internes (inner class) pour les classes imbriquées non statiques.

Contrairement aux classes classiques qui ne peuvent être définies qu’avec une visibilité public ou packages, les classes imbriquées peuvent être aussi bien de visibilité private, public, protected ou package

Classes internes 
Une classe interne peut avoir accès aux méthodes et aux attributs de la classe englobante même si ces derniers sont déclarés en private.

public class A {
    int a = 1;
    B b = new B();
    C c = new C();
    
    class B{
        void test(){
            a=a+1;
        }
    }
    class C{
        void test(){
            a=a+1;
            b.test();
        }
    }
}

La classe de test ci-dessous donnera les résultats suivants : 1, 2 et 4

    @Test
    public void testA(){
        A a = new A();
        System.out.println(a.a);
        a.b.test();
        System.out.println(a.a);
        a.c.test();
        System.out.println(a.a);
    }

Une classe interne ne peut pas exister si sa classe englobante n’existe pas. Par exemple si vous voulez tester le fonctionement de la classe B vous devrez d’abord instancier A

    @Test
    public void testB(){
        A.B b = new A().new B();
    }

ou

    @Test
    public void testB(){
       A a = new A();
        A.B b = a.new B();
    }


Static nested class : classe imbriquée statique 
Une classe statique imbriquée se comportera avec la classe englobante comme n’importe quelle autre classe de haut niveau. Elle ne peut accéder à des propriétés qui ne sont pas définies en statique.

public class A {
    static int a = 1;
    
    static class B{
        void test(){
            a=a+1;
        }
    }
}

On peut définir la classe englobée sans que la classe englobante existe.

    @Test
    public void testA(){
        System.out.println(A.a);
    }
    
    @Test
    public void testB(){       
        A.B b = new A.B();
        b.test();
        System.out.println(A.a);
    }

L’avantage est lié à l’instanciation qui ne nécessite pas une instance de la classe englobante.

Pourquoi utiliser une inner class ?
Les classes imbriquées permettent d’optimiser votre code dans plusieurs cas
  • si une classe n’est utile qu’à une autre classe il est logique de l’intégrer directement dedans. Ceci aide à la lisibilité et la maintenabilité du code
  • Si une classe a besoin d’accéder aux éléments d’une autre classe que l’on souhaite garder avec une visibilité privée, définir cette classe comme classe interne permet de répondre à cette problématique. Ceci améliore l’encapsulation
Classe anonyme interne (anonymous inner class)
Une classe anonyme en Java est une classe déclarée et instanciée en même temps sans définir de nom (c’est le compilateur qui va lui attribuer un nom). Cette classe ne peut être utilisée qu’à l’endroit où elle a été définie.

Par exemple dans l’exemple ci-dessous, nous définissons une classe anonyme interne implémentant l’interface Function de Guava

@Test
public void lengthMapping() {
 Function lengthMapper = 
                      new Function() {
  public Integer apply(String s) {
   return s==null ? 0 : s.length();
  }
 };
  Assert.assertEquals(11, (int) lengthMapper.apply("Hello World"));
}

Une classe anonyme peut accéder aux variables globales de la classe mais pas aux variables locales de la méthode où elle est déclarée. La raison en est simple c’est que les variables locales sont susceptibles d’avoir une durée de vie moins longue que la classe anonyme. En effet une variable locale est placée sur la pile (stack) le temps de l’exécution d’une méthode, alors que l’objet lié à la classe anonyme est créé en mémoire (dans la heap). Seules les variables définies en final (définies aussi dans la heap) peuvent être utilisées. Pour rappel la JVM est une machine à pile (voir la rapide comparaison entre les machines à pile ou à registres dans ma présentation de la VM Android http://javamind-fr.blogspot.fr/2012/10/dalvik-la-vm-android.html )

Classe interne locale (local inner class)
Pour être complet il reste un dernier type de classe imbriquée, les classes internes locales qui sont définies dans le corps d’une méthode. Si je reprends l’exemple précédent on pourrait par exemple créer

    @Test
    public void lengthMapping2() {
        class LengthMapper implements Function {
            public Integer apply(String s) {
                return s==null ? 0 : s.length();
            }
        };
        
        Assert.assertEquals(11, 
                    new LengthMapper().apply("Hello World").intValue());
    }

Les classes locales sont complètement cachées du monde externe.

Aucun commentaire:

Enregistrer un commentaire

Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.