CodeGym /Cours /JAVA 25 SELF /Erreurs liées aux modificateurs d'accès

Erreurs liées aux modificateurs d'accès

JAVA 25 SELF
Niveau 23 , Leçon 2
Disponible

1. Introduction

En Java, les modificateurs d'accès sont comme un système de serrures dans une maison. Ils déterminent qui peut « entrer » dans votre pièce et d'où (ou accéder au champ/méthode de votre classe). Si toutes les portes sont ouvertes, n'importe qui peut venir et modifier quelque chose. Si tout est fermé, personne ne pourra rien casser, mais vous-même pourriez vous retrouver enfermé à un moment donné.

Rappelons qu'en Java il existe quatre niveaux d'accès principaux :

Modificateur Accessible dans la classe Accessible dans le package Accessible dans les sous-classes Accessible dans les autres packages
private
(package)
protected
(via héritage)
public

(package) signifie que le modificateur n'est pas indiqué explicitement. Un tel membre de classe n'est visible qu'à l'intérieur d'un seul package.

2. Erreurs courantes avec les modificateurs d'accès

Erreur 1 : Champs et méthodes avec le modificateur par défaut (package-private)

L'erreur la plus fréquente des débutants est d'oublier d'indiquer le modificateur d'accès. En conséquence, le champ ou la méthode devient accessible dans tout le package, alors que ce n'était pas prévu. Cela peut conduire à ce qu'une autre classe (dans le même package, mais sans lien avec la vôtre) puisse modifier l'état interne de votre objet.

// Erreur : le champ name n'est pas protégé !
class User {
    String name; // package-private!
}

Par conséquent, n'importe quelle classe de ce package peut écrire :

User user = new User();
user.name = "Vasya"; // sans restriction !

Erreur 2 : Violation de l'encapsulation — champs publics (public)

La deuxième erreur la plus répandue consiste à déclarer les champs de classe en public. C'est pratique quand on débute ou pour un petit exemple, mais dans des projets réels, c'est presque toujours une mauvaise idée. Vous perdez le contrôle sur qui modifie vos données et comment.

public class Account {
    public double balance; // DANGEREUX !
}

Désormais, n'importe quel code peut faire :

Account acc = new Account();
acc.balance = -1000000; // Et qui est responsable maintenant ?

Erreur 3 : Absence de getters et setters

Parfois, un développeur rend les champs private, mais oublie d'ajouter des méthodes pour les manipuler. Il devient alors impossible d'obtenir ou de modifier la valeur, même lorsque ce serait pertinent.

public class Product {
    private String name;
    // Ni getName() ni setName()
}

Erreur 4 : Tentative d'accès à des membres private depuis une autre classe

Si vous avez déclaré un champ ou une méthode comme private, vous ne pouvez pas y accéder depuis une autre classe, même si elle se trouve dans le même package. Les débutants s'étonnent souvent de ne pas « voir » le champ.

public class User {
    private String password;
}

public class UserService {
    public void resetPassword(User user) {
        // user.password = "123"; // Erreur de compilation !
    }
}

Erreur 5 : Pièges avec protected

Beaucoup pensent que protected signifie « visible partout où il y a héritage ». Mais en Java, l'accès aux membres protected en dehors du package n'est possible que via l'héritage et uniquement pour la sous-classe. C'est une subtilité facile à manquer.

package animals;

public class Animal {
    protected void sleep() {}
}

package zoo;
import animals.Animal;

public class Dog extends Animal {
    public void test() {
        sleep(); // OK — sous-classe
    }
}

public class NotADog {
    public void test() {
        Animal a = new Animal();
        // a.sleep(); // Erreur : pas une sous-classe !
    }
}

3. Comment bien faire : bonnes pratiques

Règle 1 : Par défaut, rendez les champs private

C'est le principe clé de l'encapsulation. Les champs doivent être cachés à tout le monde sauf à la classe elle‑même. Si vous devez exposer un accès — utilisez des getters/setters.

public class Book {
    private String title;
    private int pages;

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

Règle 2 : N'ouvrez que les méthodes nécessaires

Si une méthode doit être accessible de l'extérieur — rendez‑la public. Si elle n'est nécessaire qu'à l'intérieur du package — laissez‑la package-private. Si la méthode n'est destinée qu'aux héritiers — utilisez protected.

Règle 3 : Minimisez la portée

Plus la portée est restreinte, plus le risque d'erreurs accidentelles et « d'invités inattendus » est faible. Ne rendez pas vos méthodes et champs public s'il n'y a pas de nécessité.

Règle 4 : Utilisez des getters et setters pour contrôler l'accès

Cela permet d'ajouter une logique supplémentaire lors de la lecture/écriture d'un champ, par exemple une validation.

public class Account {
    private double balance;

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("Le solde ne peut pas être négatif !");
        }
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

Règle 5 : Ne divulguez pas l'implémentation interne

Si vous avez un tableau ou une liste comme champ, ne le renvoyez pas directement via un getter — renvoyez une copie ou fournissez uniquement les méthodes nécessaires.

public class Team {
    private List<String> members = new ArrayList<>();

    // Correct :
    public List<String> getMembers() {
        return new ArrayList<>(members); // on renvoie une copie
    }
}

4. Exemples pratiques

Supposons que nous ayons une classe LibraryUser, qui décrit un usager de bibliothèque.

Exemple d'implémentation incorrecte

public class LibraryUser {
    public String name;
    public int borrowedBooks;
}

Dans cet état, n'importe quel code peut faire n'importe quoi avec l'objet :

LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // De la logique ? Quelle logique ?

Exemple d'implémentation correcte avec encapsulation

public class LibraryUser {
    private String name;
    private int borrowedBooks;

    public LibraryUser(String name) {
        this.name = name;
        this.borrowedBooks = 0;
    }

    public String getName() {
        return name;
    }

    public int getBorrowedBooks() {
        return borrowedBooks;
    }

    public void borrowBook() {
        borrowedBooks++;
    }

    public void returnBook() {
        if (borrowedBooks > 0) {
            borrowedBooks--;
        }
    }
}

Désormais, le code externe ne peut plus modifier directement le nombre de livres empruntés ni le nom de l'utilisateur. Tout est contrôlé uniquement via les méthodes de la classe.

5. Particularités et nuances d'implémentation

Parfois, il peut sembler plus simple de rendre un champ public, que d'écrire toute une série de getters et setters. Mais c'est un piège ! Un champ ouvert — c'est comme une porte d'appartement ouverte : oui, c'est pratique, mais ce n'est pas très sûr.

Autre subtilité — il n'est pas toujours nécessaire de créer des getters et setters pour tous les champs. Si la valeur d'un champ ne doit pas changer après la création de l'objet, ne fournissez qu'un getter, et déclarez le champ final :

public class Passport {
    private final String number;

    public Passport(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }
}

Rappelez‑vous également : si une classe est déclarée comme public, le nom du fichier doit correspondre au nom de la classe ! Ce n'est pas exactement à propos des modificateurs d'accès, mais c'est une erreur très fréquente chez les débutants.

6. Erreurs typiques lors de l'utilisation des modificateurs d'accès

Erreur n°1 : vous avez oublié d'indiquer le modificateur d'accès pour un champ ou une méthode. En conséquence, le champ ou la méthode devient accessible dans tout le package, même si vous ne le souhaitiez pas. Indiquez toujours explicitement le modificateur, même si l'IDE ne se plaint pas.

Erreur n°2 : tous les champs sont déclarés comme public. Cela tue l'encapsulation, rend votre code vulnérable et imprévisible. L'habitude prise dans des exemples « pour faire simple » ne doit pas se retrouver dans le code de production.

Erreur n°3 : tentative d'accéder à un champ private depuis une autre classe. Java ne le permettra pas — le compilateur vous protégera, mais si vous souhaitez « contourner » cela via la réflexion, demandez‑vous pourquoi ce besoin est apparu.

Erreur n°4 : penser que les membres protected seront accessibles partout où il y a héritage. En réalité, hors du package, on ne peut y accéder que depuis une sous‑classe et uniquement via this ou via un objet de la sous‑classe.

Erreur n°5 : renvoyer la collection interne via un getter. Si vous retournez une référence vers un tableau ou une liste interne, du code externe pourra la modifier, ce qui viole les invariants de la classe.

Erreur n°6 : absence de contrôle lors de l'affectation via un setter. Si vous ne validez pas la valeur d'entrée, vous pouvez obtenir un état incohérent de l'objet (par exemple, un solde négatif).

Erreur n°7 : portée des méthodes trop large. Parfois, on déclare des méthodes public alors qu'elles ne sont nécessaires qu'à l'intérieur du package ou de la classe. Cela expose une API superflue et complique la maintenance.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION