W3docs

Getters et setters en Java

Exposez des champs privés en toute sécurité en Java avec les méthodes getter et setter, et ajoutez de la validation dans les setters.

Un getter est une méthode qui retourne la valeur d'un champ ; un setter est une méthode qui écrit dans ce champ. Ensemble, ils constituent la manière standard d'offrir un accès contrôlé à un champ privé. Le chapitre précédent sur l'encapsulation a expliqué le pourquoi — ce chapitre traite du comment, notamment les conventions de nommage, les patterns de validation, et les cas où vous devriez les ignorer entièrement.

Le pattern de base

Un champ, un getter et un setter :

public class User {
  private String email;

  public String getEmail()              { return email; }
  public void   setEmail(String email)  { this.email = email; }
}

Par convention :

  • Le getter est nommé get<FieldName> pour tout sauf les booléens.
  • Pour un champ boolean, le getter est nommé is<FieldName> (isActive, hasPermission).
  • Le setter est nommé set<FieldName> et prend un paramètre du type du champ.
  • Les deux sont public sauf raison contraire.

Ces règles sont aussi la convention JavaBeans — de nombreux outils (bibliothèques de sérialisation, moteurs de templates, refactorisations d'IDE) s'appuient sur elles et ne parviendront tout simplement pas à voir les champs dont les accesseurs ont des noms différents.

Validation dans les setters

Un setter est votre unique chance de rejeter une mauvaise entrée avant que le champ ne soit écrit :

public class User {
  private String email;

  public void setEmail(String email) {
    if (email == null || !email.contains("@")) {
      throw new IllegalArgumentException("not a valid email: " + email);
    }
    this.email = email;
  }
}

Un setter nu qui ne fait qu'assigner n'est guère mieux qu'un champ public. L'intérêt de placer un setter entre les appelants et le champ est de pouvoir y effectuer une vérification. Si vous vous retrouvez à écrire des dizaines de setters qui ressemblent tous à this.x = x;, demandez-vous si la classe a vraiment besoin d'être mutable — un objet initialisé par constructeur avec uniquement des getters (une classe immuable ou un record) est souvent plus adapté.

Getters calculés et dérivés

Un getter n'a pas besoin de correspondre un-à-un à un champ. Il peut calculer sa valeur de retour :

public class Rectangle {
  private final double width, height;
  public Rectangle(double w, double h) { this.width = w; this.height = h; }

  public double getWidth()  { return width; }
  public double getHeight() { return height; }
  public double getArea()   { return width * height; }   // derived
}

Pour un appelant, getArea() ressemble exactement à getWidth() — ce sont toutes les deux des méthodes retournant un double. Le fait que l'une lise une valeur stockée et l'autre la calcule est un détail d'implémentation que vous pouvez modifier sans que personne ne le remarque.

Getters en lecture seule

Un getter sans setter expose un champ en lecture mais le verrouille en écriture :

public class Order {
  private final long id;
  private final long createdAt;

  public Order(long id, long createdAt) {
    this.id        = id;
    this.createdAt = createdAt;
  }

  public long getId()        { return id; }
  public long getCreatedAt() { return createdAt; }
}

Le code externe peut demander « quel est l'ID ? » mais ne peut pas le modifier. C'est ainsi que fonctionnent en pratique les champs immuables après construction.

Nommage booléen : is, has, should

Les getters booléens se lisent plus naturellement avec un préfixe is/has/can/should :

public boolean isActive()        { return active; }
public boolean hasPermission()   { return permission != null; }
public boolean canRetry()        { return retries < maxRetries; }

Combiné avec le nom du champ, le site d'appel se lit comme une phrase : if (user.isActive()) { ... }. Le setter correspondant est simplement setActive(boolean), sans le is.

Un piège à éviter : les outils JavaBeans déduisent le nom de la propriété à partir de l'accesseur, donc isActive() et getActive() correspondent tous les deux à la propriété active — mais les outils cherchent généralement les accesseurs préfixés par is uniquement pour le type primitif boolean. Pour un champ Boolean encapsulé, utilisez getActive() pour rester découvrable.

Copies défensives (encore une fois)

Si un getter retourne un objet interne mutable, retournez une copie ou une vue non modifiable :

private final List<String> tags = new ArrayList<>();

public List<String> getTags() {
  return List.copyOf(tags);
}

Pareil à l'entrée pour un setter :

public void setTags(List<String> tags) {
  this.tags.clear();
  this.tags.addAll(tags);    // copy in
}

Sans ces copies, l'appelant pourrait muter la liste interne tags et contourner tout ce que la classe fait par ailleurs.

Java moderne : des accesseurs plus courts

Le code Java récent (surtout dans les bibliothèques qui ne sont pas liées aux outils JavaBeans) supprime souvent le préfixe get par symétrie avec la façon dont les accesseurs de record sont nommés :

public class Point {
  private final int x, y;
  public Point(int x, int y) { this.x = x; this.y = y; }
  public int x() { return x; }
  public int y() { return y; }
}

Si vous n'êtes pas lié par JavaBeans (pas de Hibernate, pas de paramètres Jackson par défaut, pas de JSP), ce style est correct — et correspond à ce que record Point(int x, int y) {} générerait automatiquement. Choisissez un style par base de code et appliquez-le de manière cohérente. Pour une vue d'ensemble de l'approche style record, voir les records modernes.

Ne les générez pas par réflexe

Les IDE modernes génèrent volontiers un getter et un setter pour chaque champ en une seule touche. Résistez à cette envie. Chaque paire générée est une décision de conception que vous prenez :

  • Un getter expose un champ — voulez-vous vraiment que les appelants le lisent ?
  • Un setter expose un chemin d'écriture — voulez-vous vraiment que les appelants le modifient ?
  • Si les deux réponses sont inconditionnellement oui, pourquoi le champ est-il privé du tout ?

La bonne réponse est souvent « ni l'un ni l'autre » — remplacez setBalance(int) par deposit(int) et withdraw(int) qui expriment les opérations réelles.

Un exemple concret

java— editable, runs on the server

Et ensuite

Cela conclut les bases d'une classe unique — comment la déclarer, comment contrôler ses membres, comment exposer juste ce qu'il faut vers l'extérieur. Le prochain chapitre aborde la deuxième grande idée de la POO : l'héritage, où une classe s'appuie sur une autre au lieu de partir de zéro. Continuez vers l'héritage Java.

Pratique

Pratique
Pourquoi un setter est-il généralement préférable à rendre le champ public, même si le setter ne fait qu'assigner ?
Pourquoi un setter est-il généralement préférable à rendre le champ public, même si le setter ne fait qu'assigner ?
Was this page helpful?