Imbrication simple

Imbrication simple #

Les classes imbriquées (nested class ou inner class) mettent à disposition un mécanisme qui permet de déclarer des classes (ou interfaces) dans une autre classe ou une autre interface. Il permet de réduire la complexité de manière locale ou de retourner des objets qui n’existeraient pas sans leur objet englobant.

1
2
3
4
5
6
7
8
9
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
  • une classe interne peut être statique si un objet de celle-ci ne possède aucune référence de sa classe englobante.
  • un objet d’une classe qui n’est pas statique est rattaché à une instance existante de la classe englobante.

Exemple d’utilisation #

Nous souhaitons réaliser un automate fini (finite state machine) composé de deux états tout en ayant une référence qui puisse pointer sur un des deux. Un objet State peut être de deux types : InitialState ou SecondState et se traduirait en notation BNF par :

State ::= InitialState | SecondState

illustré à l’aide de ce graphe:

graph TD A[InitialState] -->|nextState| B(SecondState) B --> |nextState| A

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Mise en oeuvre d'une machine d'états finis
public interface State {
    static class InitialState implements State {
      public boolean isRunning() { return false; }
      public State nextState() { return new SecondState(); }
    }
    static class SecondState implements State {
      public boolean isRunning() { return true; }
      public State nextState() { return new InitialState(); }
    }
    default boolean isRunning() { return true; }
    public static State initial() { return new InitialState(); }
    State nextState();
}

InitialState et SecondState sont indépendants d’une quelconque instance englobante. Ils sont donc statiques.

Quiz #

Que retourne ces deux expressions ?

1
2
State.initial().nextState().isRunning();
State.initial().nextState().nextState().isRunning();

Exemples connus #

Map.Entry (statique) #

Les interfaces peuvent également être imbriquées. Entry<K,V> est une interface statique interne à l’interface Map<K,V>. Elle représente une entrée d’un tableau associatif, autrement dit, un couple clé-valeur. L’interface Map fournit une méthode statique qui retourne un objet d’une classe respectant l’interface Entry. L’objet retourné est immutable ! L’appel à la méthode setValue retourne une exception (UnsupportedOperationException).

1
2
3
Map.Entry<Integer, String> oneEntry = Map.entry(1, "one");
int key = oneEntry.getKey(); // key = 1
String value = oneEntry.getValue(); // value = "one"

Un import static permet de simplifier la syntaxe de la méthode entry pour peupler un tableau associatif sans devoir préciser le type englobant :

1
2
3
4
5
6
7
import static java.util.Map.entry;
import static java.util.Map.ofEntries;

Map<Integer, String> map = ofEntries( // au lieu de Map.ofEntries()
    entry(1, "one"), // au lieu de Map.entry()
    entry(2, "two")
)

Iterator (non statique) #

L’itérateur que vous verrez au prochain chapitre est un exemple d’une classe interne non statique. Elle doit référencer une collection existante:

1
2
3
4
5
6
7
Iterator<String> it = List.of("Hello", "world").iterator();
// it possède une référence sur la liste List.of("Hello", "world")

while (it.hasNext()) {
    String oneString = it.next();
    /* do something with oneString */
}











comments powered by Disqus