Skip to content

Encapsulation et portée

Vue d’ensemble rapide

Modificateur Accessible depuis… Pour quoi ?
public Partout (même autres packages) API publiques, méthodes utilitaires, classes « visibles »
protected Le même package + les sous-classes (même si elles sont dans un autre package) Points d’extension pour l’héritage
(aucun) (package-private) Uniquement le même package Détails internes d’un module/package
private Uniquement dans la classe (et ses classes internes) Encapsulation stricte des données/implémentations

Rappel important : Pour une classe de premier niveau, seuls public et (aucun) sont autorisés (pas private/protected).


Portée vs. Modificateurs d’accès (à ne pas confondre)

  • Portée (scope) : « Où, dans le fichier/bloc, un nom est-il visible ? » (blocs {}, méthodes, etc.).
  • Modificateurs d’accès : « Quelles autres classes/packages peuvent y accéder ? » (private, package‑private, protected, public).
    On peut avoir une portée large dans la classe (p. ex. un champ) mais un accès restreint (private).

Exemple compact dans un seul package

package demo;

public class Compte {
    public String iban;          // visible partout (à éviter pour un champ !)
    protected double solde;      // visible dans demo.* et dans les sous-classes
    String banque;               // package-private : visible dans demo.* seulement
    private String pin;          // visible seulement dans Compte

    public Compte(String iban, String pin) {
        this.iban = iban;
        this.pin = pin;
    }

    public void depot(double montant) { // méthode publique (API)
        solde += montant;
    }

    private boolean pinValide(String essai) { // aide interne
        return pin.equals(essai);
    }
}

class OutilInterne { // pas de modificateur => package-private
    static void sync(Compte c) {
        // c.banque (OK, même package)
        // c.pin (ERREUR : private)
    }
}

Points pédagogiques : - Éviter les champs publics : préférer private + getters/setters (ou immutabilité). - package-private (aucun mot-clé) est très utile pour les utilitaires internes d’un package.


Exemple inter-packages pour protected

p1/Base.java

package p1;

public class Base {
    protected int x = 42;
    int y = 7; // package-private (p1 seulement)

    protected void hook() { /* extension point */ }
}

p2/Derive.java

package p2;

import p1.Base;

public class Derive extends Base {
    void test() {
        int a = this.x;   // OK : protected + sous-classe (autre package)
        hook();           // OK

        // int b = this.y; // ERREUR : y est package-private dans p1
    }
}

p2/HorsSousClasse.java

package p2;

import p1.Base;

public class HorsSousClasse {
    void test() {
        Base b = new Base();
        // b.x;    // ERREUR : protected n’est pas visible ici (pas sous-classe)
        // b.y;    // ERREUR : package-private (p1 seulement)
    }
}

Points pédagogiques : - protected est visible dans le package d’origine et dans les sous-classes, même situées dans un autre package. - Hors sous-classe (et hors package), protected n’est pas accessible.


Bonnes pratiques (règles simples)

  1. Par défaut : private pour champs et méthodes d’implémentation.
    → Exposez une API publique minimale (constructeurs/méthodes nécessaires).
  2. package-private pour partager des détails entre classes d’un même package sans les exposer à l’extérieur.
  3. protected seulement si vous préparez une extension par héritage (hooks, template method, etc.).
    → Sinon, préférez la composition à l’héritage.
  4. public pour la surface d’API stable : classes, constructeurs et méthodes que d’autres packages doivent utiliser.

Pièges fréquents

  • Champs public : fuyez (viol d’encapsulation, invariants fragilisés).
  • Confusion protected : ce n’est pas « public pour tout le monde » — c’est package + sous-classes.
  • Classes de premier niveau protected/private : interdit.
  • Tests unitaires : si les tests sont dans le même package, package‑private suffit souvent (inutile de tout mettre en public).