Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 20

Redéfinir toString()

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons fait la distinction entre les membres static qui appartiennent à la classe dans son ensemble, et les membres d'instance, propres à chaque objet.

Aujourd'hui, nous allons nous concentrer sur la manière dont un objet se "présente" au monde extérieur. Nous allons étudier la méthode toString(), une méthode fondamentale héritée par chaque objet en Java. L'objectif est d'apprendre à la redéfinir pour obtenir une représentation textuelle claire et utile de l'état d'un objet. C'est une compétence essentielle pour le débogage et la journalisation (logging), des activités quotidiennes pour un développeur backend.

1. Qu'est-ce que la méthode toString() ?

Lorsque vous essayez d'afficher un objet directement, que se passe-t-il ?

public class Produit {
    private String nom;
    private double prix;

    public Produit(String nom, double prix) {
        this.nom = nom;
        this.prix = prix;
    }

    // Pas de méthode toString() redéfinie pour l'instant
}

public class Main {
    public static void main(String[] args) {
        Produit monProduit = new Produit("Stylo", 1.5);
        System.out.println(monProduit);
    }
}

Si vous exécutez ce code, la console affichera quelque chose de peu compréhensible, comme Produit@1b6d3586.

Pourquoi ? Parce que chaque classe en Java hérite implicitement de la classe java.lang.Object. Cette classe mère fournit une implémentation par défaut de la méthode toString().

Méthode Java toString()

Cet article de CodeGym explique l'origine de la méthode toString() et son comportement par défaut.

Lisez la première partie de l'article, jusqu'à la section "Exemple de méthode Java toString()". Concentrez-vous sur la sortie par défaut (package.class@hashCode) et la raison pour laquelle elle n'est généralement pas utile.

Comme vous l'avez lu, le résultat par défaut est le nom de la classe suivi du symbole @ et du "hash code" de l'objet en hexadécimal. C'est une référence mémoire, mais cela ne nous dit rien sur le contenu de notre objet monProduit (son nom est "Stylo", son prix est 1.5).

Pour rendre cette sortie utile, nous devons fournir notre propre implémentation de la méthode toString().


2. Redéfinir la méthode toString()

"Redéfinir" (ou override en anglais) signifie qu'une classe enfant fournit une implémentation spécifique pour une méthode qui est déjà définie dans sa classe parente. Dans notre cas, nous allons redéfinir la méthode toString() héritée de Object.

Pour ce faire, nous ajoutons une méthode avec exactement la même signature (public String toString()) à notre classe. Il est fortement recommandé d'utiliser l'annotation @Override juste avant la méthode. Cette annotation indique au compilateur notre intention de redéfinir une méthode. Si nous faisons une erreur de frappe dans le nom de la méthode, par exemple, le compilateur nous signalera une erreur, car il ne trouvera pas de méthode à redéfinir.

Reprenons notre classe Produit :

public class Produit {
    private String nom;
    private double prix;

    public Produit(String nom, double prix) {
        this.nom = nom;
        this.prix = prix;
    }

    @Override
    public String toString() {
        return "Produit{nom='" + this.nom + "', prix=" + this.prix + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        Produit monProduit = new Produit("Stylo", 1.5);
        System.out.println(monProduit);
    }
}

Résultat :

Produit{nom='Stylo', prix=1.5}

C'est bien plus informatif ! Nous voyons maintenant l'état interne de notre objet.

Comprendre et Utiliser toString() en Java : Exercices Corrigés

Cet article de LeCoursGratuit.com vous guide à travers un exemple complet.

Lisez les sections "Exercice 1 : Implémentation de la méthode toString()" et "Exercice 2 : Utilisation de la méthode toString()". L'exemple avec la classe Personne est très similaire à ce que nous venons de faire et renforcera votre compréhension.


3. L'appel implicite à toString()

Un point crucial à comprendre est que vous appelez rarement monProduit.toString() explicitement. Java le fait pour vous dans certaines situations.

Les deux cas les plus courants sont :

  1. Lorsque vous passez un objet à System.out.println().
  2. Lorsque vous concaténez un objet à une chaîne de caractères avec l'opérateur +.
Produit monProduit = new Produit("Cahier", 3.0);

// Appel implicite dans println
System.out.println(monProduit); 

// Appel implicite dans la concaténation de chaînes
String description = "Détails de l'article : " + monProduit;
System.out.println(description);

Les deux lignes ci-dessus produiront une sortie lisible grâce à notre méthode toString() redéfinie.

Méthode Java toString()

Revenez à l'article de CodeGym et lisez la dernière partie.

Observez la différence entre l'appel explicite (mySum.toString()) et l'appel implicite (System.out.println(mySum)). L'article montre également ce qui se passerait si la méthode toString() n'était pas redéfinie, confirmant ce que nous avons vu au début.


4. Bonnes pratiques et génération par l'IDE

En tant que développeur, vous n'écrirez que rarement la méthode toString() manuellement. Les environnements de développement intégrés (IDE) comme IntelliJ IDEA peuvent la générer pour vous en quelques clics. C'est une pratique professionnelle qui vous fait gagner du temps et évite les erreurs de frappe.

Dans IntelliJ IDEA :

  1. Placez votre curseur à l'intérieur de votre classe.
  2. Utilisez le raccourci Alt + Ins (Windows/Linux) ou Cmd + N (macOS) pour ouvrir le menu "Generate".
  3. Sélectionnez toString().
  4. Choisissez les champs que vous souhaitez inclure dans la sortie.
Cette image montre la boîte de dialogue "Generate" d'IntelliJ. Vous pouvez sélectionner les attributs à inclure (ici, pour une classe Beer) et même choisir un modèle pour le format de la chaîne de caractères. Le format standard `String concat (+)` est un excellent point de départ.

Conseil de pro : Pour des classes avec de nombreux attributs, l'utilisation de StringBuilder est plus performante que la concaténation avec + car elle évite de créer de multiples objets String intermédiaires. Les IDE modernes proposent souvent un modèle de génération toString() basé sur StringBuilder.


5. Exercice pratique : Représenter un Utilisateur

Mettons cela en pratique. Vous allez reprendre la classe Utilisateur que nous avons pu utiliser dans des leçons précédentes et lui donner une représentation textuelle digne de ce nom.

Énoncé :

  1. Créez une classe Utilisateur avec les attributs privés suivants :
    • long id
    • String username
    • String email
    • boolean isActive
  2. Créez un constructeur qui initialise tous ces attributs.
  3. Redéfinissez la méthode toString() pour qu'elle retourne une chaîne de caractères descriptive. Le format doit être similaire à :
    Utilisateur[id=1, username='jdoe', email='jdoe@example.com', isActive=true]
    • Astuce : Essayez de la générer automatiquement avec IntelliJ IDEA !
  4. Dans votre méthode main, créez au moins deux instances de Utilisateur avec des valeurs différentes.
  5. Affichez chaque objet directement dans la console en utilisant System.out.println() pour vérifier que votre méthode toString() fonctionne correctement.
Cliquez ici pour voir la solution

Classe Utilisateur.java

public class Utilisateur {
    private long id;
    private String username;
    private String email;
    private boolean isActive;

    public Utilisateur(long id, String username, String email, boolean isActive) {
        this.id = id;
        this.username = username;
        this.email = email;
        this.isActive = isActive;
    }

    // Méthode générée par IntelliJ IDEA
    @Override
    public String toString() {
        return "Utilisateur[" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", isActive=" + isActive +
                ']';
    }
}

Classe Main.java

public class Main {
    public static void main(String[] args) {
        Utilisateur user1 = new Utilisateur(101, "alice_dev", "alice@web.com", true);
        Utilisateur user2 = new Utilisateur(102, "bob_qa", "bob@web.com", false);

        System.out.println("Détails de l'utilisateur 1 :");
        System.out.println(user1); // Appel implicite à user1.toString()

        System.out.println("\nDétails de l'utilisateur 2 :");
        System.out.println(user2); // Appel implicite à user2.toString()
    }
}

Résultat attendu dans la console :

Détails de l'utilisateur 1 :
Utilisateur[id=101, username='alice_dev', email='alice@web.com', isActive=true]

Détails de l'utilisateur 2 :
Utilisateur[id=102, username='bob_qa', email='bob@web.com', isActive=false]

Conclusion

Cette leçon clôture notre module sur les bases des classes, des objets et de l'encapsulation. Vous savez maintenant non seulement comment structurer un objet avec des attributs et des méthodes, mais aussi comment lui fournir une "voix" pour décrire son propre état.

Points clés à retenir :

  • Tout objet Java hérite de la méthode toString() de la classe Object.
  • L'implémentation par défaut n'est pas informative ; elle affiche le nom de la classe et un hash code.
  • Nous devons redéfinir (@Override) la méthode toString() pour retourner une chaîne de caractères décrivant l'état de l'objet (ses attributs importants).
  • toString() est appelée implicitement lors de l'affichage d'un objet (System.out.println) ou de sa concaténation à une chaîne.
  • Les IDE comme IntelliJ IDEA peuvent générer automatiquement une implémentation de toString(), ce qui est la pratique professionnelle courante.

Dans le prochain module, nous allons pousser la programmation orientée objet un cran plus loin. Nous aborderons les concepts puissants d'héritage, de polymorphisme et d'abstraction. L'annotation @Override que vous avez utilisée aujourd'hui est d'ailleurs une première porte d'entrée vers le polymorphisme, que nous allons explorer en détail très prochainement.