IntelliJ IDEA Java Tips & Tricks

Bonnes pratiques pour la programmation Java

Read this post in other languages:

Créer du code de qualité requiert de suivre certaines règles. Dans cet article, nous partageons plusieurs bonnes pratiques, conseils et astuces incontournables, qui s’appliquent au développement logiciel en général et aux spécificités des projets Java. C’est parti !

Conseils d’ordre général

Il est important de garder à l’esprit les règles générales suivantes. 

Privilégier la clarté

Le code doit avant tout être compréhensible et facile à maintenir, plutôt que servir à mettre en avant ses compétences techniques. Un code clair et concis permet de concevoir des logiciels plus faciles à déboguer, à maintenir et à faire évoluer, pour le bénéfice de toutes les personnes impliquées dans le projet. La complexité du code n’apporte aucun avantage, contrairement à simplicité et à la lisibilité.

Prenons les exemples suivants :

  • Code complexe : 

C’est une façon inhabituelle d’échanger les valeurs des variables « a » et « b ». Elle est certes intelligente, mais peut être difficile à comprendre au premier abord.

  • Code clair : 

Voici une approche plus courante. Bien qu’elle requière une ligne de code supplémentaire, elle est bien plus facile à comprendre pour la plupart des programmeurs. 

Rester concis

Assurez-vous que les méthodes et les classes ne soient pas trop longues. Bien qu’il n’y ait pas de règles strictes concernant le nombre exact de lignes ou de mots pour une classe, il est recommandé de conserver une structure ciblée et cohérente. En ce qui concerne les méthodes, il est généralement conseillé d’avoir entre 10 et 20 lignes de code par méthode. Si une méthode devient trop longue, il est préférable de la diviser en sections plus petites et plus faciles à gérer.

Si vous souhaitez vous entraîner à identifier les méthodes qui sont trop longues, voici une excellente vidéo d’Emily Bache, coach technique

IntelliJ IDEA peut également vous aider à vous débarrasser des méthodes et classes trop longues au moyen de différentes options de refactorisation. Il vous permet, par exemple, d’extraire des méthodes pour fractionner une méthode longue en plusieurs méthodes plus courtes.

Prêter attention au nommage

Les noms propres des méthodes et des variables servent à comprendre le but et la fonction de votre code, ce qui est essentiel pour une communication efficace. Voici ce que vous devez savoir sur les conventions de nommage les plus importantes. 

Pour favoriser la compréhension et la communication, nous vous conseillons d’éviter les variables à une seule lettre, de vous assurer que les noms des méthodes illustrent bien leurs actions et d’aligner les noms des objets et des champs avec le domaine d’activité concerné. Par exemple, une méthode nommée calculPrixTotal() indique immédiatement son objectif, tandis qu’un nom tel que calcul() est trop imprécis. De même, une variable nommée customerEmailAddress est explicite, alors que l’utilisation de l’abréviation cea pourrait prêter à confusion.

Pour prendre un autre exemple, au lieu de nommer une variable simplement timeout, il est préférable de spécifier à quelle unité de temps elle se réfère. On utilisera donc plutôt un nom comme timeoutInMs ou timeoutInMilliseconds pour éviter toute confusion concernant les unités.

Tester, tester, tester 

Le code doit être testé pour s’assurer que l’application fonctionne comme prévu et continuera à fonctionner même si on y apporte des modifications. Les tests permettent d’identifier les problèmes au plus tôt et de les corriger plus rapidement et facilement. Ils aident aussi à comprendre comment le code est censé fonctionner et réduisent les risques d’erreurs lorsqu’il est mis à jour par la suite. 

Il est essentiel que les noms de tests soient explicites, car ils indiquent ce que chaque test fait et ce qu’il recherche. Par exemple, nommer un test utilisé pour alerter lorsqu’un email est manquant AlertWhenEmailIsMissing()checks permet de comprendre immédiatement sur quoi il porte et évite de perdre du temps à entrer dans les détails. 

Nous vous invitons à lire cet article de Marit van Dijk pour plus d’informations sur les tests. 

Conseils spécifiques au langage Java

Les conseils et astuces suivants vous aideront à améliorer votre code en évitant certains pièges courants en Java. 

Utiliser des expressions switch plutôt qu’un grand nombre d’instructions If

L’utilisation d’expressions switch peut rendre votre code plus lisible et organisé en consolidant plusieurs conditions dans une seule structure. Cette approche simplifie le code et facilite sa compréhension et sa maintenance. 

Regardons l’exemple suivant, qui porte sur différents types de crème glacée et leurs principaux ingrédients.

  • Nombre excessif de conditions else-if

Dans cet exemple, le code utilise une chaîne d’instructions else–if pour associer un parfum de crème glacée à ses principaux ingrédients. Plus le nombre de parfums augmente, plus le nombre d’instructions if risque de devenir ingérable et de rendre le code difficile à lire.

  • Switch

Dans cet autre exemple, nous utilisons une expression switch pour parvenir au même résultat qu’avec de multiples conditions if-else. L’expression switch est plus compacte, plus nette et plus facilement compréhensible lorsque l’on compare une variable unique à plusieurs valeurs constantes.

IntelliJ IDEA fournit une inspection spéciale qui permet de transformer vos instructions if en expressions switch en quelques secondes. 

Vous trouverez des exemples d’utilisation de switch intéressants dans cet article de Mala Gupta, Java Developer Advocate. 

Éviter les blocs catch vides

Les blocs catch vides en Java correspondent à des clauses catch qui ne contiennent pas de code pour gérer les exceptions. Si une exception est identifiée par l’un de ces blocs, rien ne se passe et le programme continue comme aucune erreur ne s’était produite. Cela peut rendre l’identification de problèmes et le débogage difficiles.

  • Bloc catch vide

Dans cet exemple, l’exception est détectée, mais rien n’est fait. 

IntelliJ IDEA met de tels cas en évidence avec une inspection et propose des solutions : 

  • Journaliser l’exception

Une façon de gérer l’exception est de la journaliser en utilisant e.printStackTrace(), qui imprime la trace de la pile sur la console, ce qui aide à identifier et à déboguer les problèmes potentiels.

  • Journaliser l’exception et la relancer

Dans un monde idéal, le bloc catch identifierait IOException, puis imprimerait un message d’erreur sur la console et renverrait l’exception pour la traiter plus tard. 

De cette façon, vous pouvez avoir une vision complète de ce qui s’est mal passé.

  • Journaliser l’exception et renvoyer une autre valeur

Une autre manière de résoudre le problème des blocs catch vides est de journaliser l’exception mais de renvoyer une valeur significative. 

Préférer les collections aux tableaux pour plus de flexibilité

Si les tableaux Java sont efficaces et faciles à utiliser, leur taille est fixée lors de leur création, ce qui limite le nombre d’opérations et les rend difficile à utiliser pour différentes manipulations de données. 

Les collections en Java offrent bien plus de flexibilité et d’utilitaires, comme ArrayList ou HashSet. Par exemple, ArrayList offre le redimensionnement dynamique et de nombreuses méthodes utilitaires, et il est plus facile à utiliser, particulièrement avec les génériques. Voyons cela avec des exemples de code : 

  • Tableaux

Nous avons créé un tableau de chaînes. La taille des tableaux Java est fixe, si nous voulions ajouter un onzième élément, nous devrions créer un autre tableau et copier tous les éléments.

  • Collections

Comme alternative au code précédent, on peut utiliser la classe ArrayList. Les collections telles que ArrayList peuvent croître et diminuer lors de l’exécution, ce qui offre plus de flexibilité. Elles s’accompagnent aussi de méthodes puissantes pour manipuler les données, telles que .add(), .remove(), .contains(), .size(), entre autres.

Opter pour l’immuabilité

Les objets immuables sont des objets dont l’état ne peut pas être modifié après leur création. Ils permettent d’écrire du code plus sûr et plus propre en supprimant la complexité liée au suivi des changements d’états muables. Cela réduit le risque de bugs et d’effets secondaires indésirables, garantit un comportement constant et simplifie le processus de débogage et la maintenance de l’application. En Java, l’immuabilité s’obtient en utilisant final. 

  • Sans final

Dans cet exemple, nous allons créer la classe Car (voiture) et imprimer la marque et le modèle. Ensuite, nous allons modifier le modèle de la voiture et l’imprimer à nouveau. La sortie de la console montre que l’état de la classe Car a changé, illustrant le comportement mutable.

  • Avec final 

Dans la version améliorée du code ci-dessous, vous pouvez constater que nous avons fait la même chose, mais nous ne pouvons plus modifier le modèle ou la marque de la voiture, car nous n’avons pas de méthode setter et la classe Car est finale. La sortie de la console montre que l’état de la classe Car reste le même, illustrant le comportement immuable.

Préférer la composition à l’héritage

En Java, il est généralement préférable d’utiliser la composition (avoir une référence ou une dépendance sur un objet d’une autre classe) plutôt que l’héritage (créer une sous-classe à partir d’une superclasse). La composition rend le code plus flexible et plus facile à tester.

  • Héritage

Dans notre exemple, GamingComputer hérite de BasicComputer. Cela peut causer des problèmes, car si BasicComputer changeait, cela pourrait casser le code de GamingComputer. De plus, GamingComputer ne peut être qu’un type de BasicComputer, ce qui limite sa flexibilité.

  • Composition 

Dans le deuxième exemple, la classe Computerest est composée de Memory et de Processor, qui sont des instances de classe séparées agissant comme des champs. Chacune de ces classes définit ses méthodes et ses comportements de façon indépendante. Cette approche est plus flexible, car vous pouvez échanger différents types Memory ou Processor ou modifier leur comportement lors de l’exécution sans modifier la classe Computer.

Pour plus d’exemples, consultez Easy Hacks: How to Create Inheritance in Java.

Optimisez les interfaces fonctionnelles grâce aux lambdas

Les interfaces fonctionnelles en Java sont un type d’interface n’ayant qu’une méthode abstraite. Les lambdas fournissent un moyen élégant et expressif de les implémenter sans le code répétitif des classes anonymes. 

  • Sans lambdas

Dans l’exemple ci-dessous, nous utilisons une classe interne anonyme afin d’implémenter l’interface Comparator pour le tri. Cela prend beaucoup de place et risque de devenir illisible avec des interfaces plus complexes.

  • Avec lambdas

Ce code fait la même chose que dans l’exemple ci-dessus, mais nous utilisons une fonction lambda au lieu d’une classe anonyme. Cela rend le code beaucoup plus compact et intuitif.

Utiliser des boucles for ou des flux améliorés

Les flux et les boucles for améliorées (boucles for-each) en Java offrent des moyens plus lisibles et compacts d’itérer sur des collections ou des tableaux par rapport à une boucle for classique.

  • Boucle for classique 

Dans ce code, nous utilisons une boucle for pour parcourir la liste. Cela requiert un compteur, la gestion de l’index des éléments et la définition de la condition d’arrêt, et tout cela augmente la complexité.

  • Boucle for améliorée

Cette boucle (forEach) élimine la nécessité de recourir aux compteurs et nous fournit directement chaque élément de la liste, ce qui simplifie le code et réduit le risque d’erreurs. Vous pouvez l’appliquer à l’aide d’une inspection dans IntelliJ IDEA. 

  • Flux

L’utilisation des streams nous donne non seulement chaque élément, comme la boucle for améliorée, mais également nous permet de réaliser des opérations complexes comme le filtrage et le mappage.

Préserver les ressources avec les instructions try-with-resources

Les instructions try-with-resources permettent de s’assurer que chaque ressource est correctement fermée après utilisation. Omettre de fermer les ressources dans un bloc try peut causer des problèmes de mémoire et des erreurs dans l’application pouvant affecter les performances et la fiabilité.

  • Fermeture manuelle des ressources

Dans l’exemple ci-dessous, nous traitons la ressource système FileInputStream manuellement, ce qui risque d’engendrer des fuites des ressources et d’autres problèmes si une exception est levée lors de la fermeture. 

  • Utilisation d’instructions try-with-resources

Dans la version améliorée, déclarer FileInputStream dans le bloc try signifie que Java le fermera automatiquement, que nous quittions le bloc try normalement ou avec une exception.

Simplifier du code fortement imbriqué

Un code fortement imbriqué est le signe de problèmes de logique qui peuvent être difficiles à remarquer au premier abord. Cela est souvent dû à l’utilisation de nombreuses instructions conditionnelles, qui sont essentielles en programmation, et dont on ne peut pas simplement se débarrasser. Il n’en reste pas moins qu’il faut trouver une solution pour simplifier le code.

  • Nombreuses instructions conditionnelles

Vous pouvez voir à quel point utiliser beaucoup de conditionnements imbriqués paraît étrange. 

  • Code refactorisé

Nous avons utilisé la technique des « clauses de garde » pour éliminer les instructions conditionnelles imbriquées. En quittant la fonction lorsque certaines conditions sont réunies, nous préservons la logique, mais avec un code à l’apparence beaucoup plus propre.

Adaptation au projet 

Nous avons également plusieurs conseils à partager sur les choses à faire et à ne pas faire lorsqu’on travaille sur des projets ayant de nombreuses dépendances. 

Maintenir les dépendances à jour 

N’oubliez pas de tenir les dépendances de votre projet à jour pour améliorer la sécurité, introduire de nouvelles fonctionnalités et corriger les bugs. Des mises à jour régulières garantissent que votre projet fonctionne sans problème et reste compatible avec d’autres outils. 

IntelliJ IDEA peut vous aider dans cette démarche. Commencez par installer le plugin Package Search disponible sur la Marketplace JetBrains via Preferences/Settings | Plugins. Naviguez ensuite jusqu’à la fenêtre d’outils Dependencies pour voir l’ensemble des dépendances dans votre projet et cliquez sur le lien Upgrade situé à côté. 

Détecter les dépendances et les API vulnérables

Analyser régulièrement votre projet pour détecter d’éventuels points faibles dans les dépendances et les API permet de réduire les risques en matière de sécurité, de respecter les règles prédéfinies et d’assurer le bon fonctionnement de votre projet. En résolvant rapidement ces vulnérabilités, vous protégez votre projet et ses utilisateurs des menaces potentielles.

Pour trouver des dépendances vulnérables avec IntelliJ IDEA, naviguez vers Code | Analyze Code et sélectionnez Show Vulnerable Dependencies. Les résultats s’affichent dans l’onglet Vulnerable Dependencies de la fenêtre d’outils Problems.

Vous pouvez également faire un clic droit sur un dossier ou un fichier, comme pom.xml ou build.gradle, dans la fenêtre d’outils Project, et choisir Analyze Code | Show Vulnerable Dependencies dans le menu contextuel. 

IntelliJ IDEA mettra en évidence les vulnérabilités dans pom.xml ou build.gradle même si elles ne sont pas explicitement vérifiées. 

Éviter les dépendances circulaires 

Les dépendances circulaires proviennent de la dépendance mutuelle entre des parties de votre projet dans une boucle. Par exemple, la partie A requiert quelque chose de la partie B, mais la partie B a aussi besoin d’autre chose de la partie A. Cela peut rendre votre projet confus et difficile à appréhender, car il est compliqué de déterminer où une partie commence et où l’autre se termine. Il est préférable d’éviter ces boucles afin que les choses restent claires et faciles à gérer.

Pour éviter les dépendances circulaires, vous pouvez utiliser une matrice de dépendances afin de visualiser les dépendances entre les composants dans vos projets.

Conclusion 

Nous espérons que ces conseils vous seront utiles pour simplifier vos tâches quotidiennes et vous inciteront à écrire un code plus simple, clair et professionnel, afin de gagner en efficacité. 

IntelliJ IDEA aide à trouver la plupart des éléments mentionnés dans cet article et fournit des correctifs automatiques. Essayez-le et dites-nous ce que vous en pensez !

Auteur de l’article original en anglais :

Delphine Massenhove

Irina Mariasova

image description

Discover more