Un guide des constructeurs en Java

1. Introduction

Les constructeurs sont les gardiens de la conception orientée objet .

Dans ce tutoriel, nous verrons comment ils agissent comme un emplacement unique à partir duquel initialiser l'état interne de l'objet en cours de création.

Allons de l'avant et créons un objet simple qui représente un compte bancaire.

2. Création d'un compte bancaire

Imaginez que nous devions créer une classe qui représente un compte bancaire. Il contiendra un nom, une date de création et un solde.

En outre, nous allons l' emporter sur les toString méthode pour imprimer les détails de la console:

class BankAccount { String name; LocalDateTime opened; double balance; @Override public String toString() { return String.format("%s, %s, %f", this.name, this.opened.toString(), this.balance); } } 

Désormais, cette classe contient tous les champs nécessaires pour stocker des informations sur un compte bancaire, mais elle ne contient pas encore de constructeur.

Cela signifie que si nous créons un nouvel objet, les valeurs du champ ne seront pas initialisées:

BankAccount account = new BankAccount(); account.toString(); 

L'exécution de la méthode toString ci-dessus entraînera une exception car le nom des objets et l' ouverture sont toujours nuls :

java.lang.NullPointerException at com.baeldung.constructors.BankAccount.toString(BankAccount.java:12) at com.baeldung.constructors.ConstructorUnitTest .givenNoExplicitContructor_whenUsed_thenFails(ConstructorUnitTest.java:23) 

3. Un constructeur sans argument

Corrigeons cela avec un constructeur:

class BankAccount { public BankAccount() { this.name = ""; this.opened = LocalDateTime.now(); this.balance = 0.0d; } } 

Remarquez quelques choses sur le constructeur que nous venons d'écrire. Tout d'abord, c'est une méthode, mais elle n'a pas de type de retour. C'est parce qu'un constructeur retourne implicitement le type de l'objet qu'il crée. L'appel de new BankAccount () maintenant appellera le constructeur ci-dessus.

Deuxièmement, il ne prend aucun argument. Ce type particulier de constructeur est appelé un constructeur o-argument .

Pourquoi n'en avons-nous pas besoin pour la première fois? C'est parce que lorsque nous n'écrivons explicitement aucun constructeur, le compilateur ajoute un constructeur par défaut sans argument .

C'est pourquoi nous avons pu construire l'objet la première fois, même si nous n'avons pas écrit explicitement un constructeur. Le constructeur par défaut, sans argument, définira simplement tous les membres sur leurs valeurs par défaut.

Pour les objets, c'est null, ce qui a abouti à l'exception que nous avons vue précédemment.

4. Un constructeur paramétré

Maintenant, un réel avantage des constructeurs est qu'ils nous aident à maintenir l' encapsulation lors de l'injection d'état dans l'objet.

Donc, pour faire quelque chose de vraiment utile avec ce compte bancaire, nous devons être en mesure d'injecter des valeurs initiales dans l'objet.

Pour ce faire, écrivons un constructeur paramétré , c'est-à-dire un constructeur qui prend quelques arguments :

class BankAccount { public BankAccount() { ... } public BankAccount(String name, LocalDateTime opened, double balance) { this.name = name; this.opened = opened; this.balance = balance; } } 

Nous pouvons maintenant faire quelque chose d'utile avec notre classe BankAccount :

 LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00); BankAccount account = new BankAccount("Tom", opened, 1000.0f); account.toString(); 

Notez que notre classe a maintenant 2 constructeurs. Un constructeur explicite, sans argument et un constructeur paramétré.

Nous pouvons créer autant de constructeurs que nous le souhaitons, mais nous aimerions probablement ne pas en créer trop. Ce serait un peu déroutant.

Si nous trouvons trop de constructeurs dans notre code, quelques modèles de conception créative peuvent être utiles.

5. Un constructeur de copie

Les constructeurs ne doivent pas être limités à la seule initialisation. Ils pourraient également être utilisés pour créer des comportements. Imaginez que nous ayons besoin de pouvoir créer un nouveau compte à partir d'un compte existant.

Le nouveau compte doit avoir le même nom que l'ancien compte, la date de création du jour et aucun fonds. Nous pouvons le faire en utilisant un constructeur de copie :

public BankAccount(BankAccount other) { this.name = other.name; this.opened = LocalDateTime.now(); this.balance = 0.0f; } 

Maintenant, nous avons le comportement suivant:

LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00); BankAccount account = new BankAccount("Tim", opened, 1000.0f); BankAccount newAccount = new BankAccount(account); assertThat(account.getName()).isEqualTo(newAccount.getName()); assertThat(account.getOpened()).isNotEqualTo(newAccount.getOpened()); assertThat(newAccount.getBalance()).isEqualTo(0.0f); 

6. Un constructeur enchaîné

Bien sûr, nous pourrons peut-être déduire certains des paramètres du constructeur ou donner à certains d'entre eux des valeurs par défaut.

Par exemple, nous pourrions simplement créer un nouveau compte bancaire avec uniquement le nom.

Alors, créons un constructeur avec un paramètre de nom et donnons aux autres paramètres les valeurs par défaut:

public BankAccount(String name, LocalDateTime opened, double balance) { this.name = name; this.opened = opened; this.balance = balance; } public BankAccount(String name) { this(name, LocalDateTime.now(), 0.0f); }

Avec le mot - clé this, nous appelons l'autre constructeur.

Nous devons nous rappeler que si nous voulons enchaîner un constructeur de superclasse, nous devons utiliser super au lieu de cela .

Souvenez-vous également que cette ou super expression doit toujours être la première déclaration.

7. Types de valeur

Une utilisation intéressante des constructeurs en Java est la création d' objets de valeur . Un objet de valeur est un objet qui ne change pas son état interne après l'initialisation.

Autrement dit, l'objet est immuable . L'immuabilité en Java est un peu nuancée et des précautions doivent être prises lors de la création d'objets.

Allons-y et créons une classe immuable:

class Transaction { final BankAccount bankAccount; final LocalDateTime date; final double amount; public Transaction(BankAccount account, LocalDateTime date, double amount) { this.bankAccount = account; this.date = date; this.amount = amount; } } 

Notez que nous utilisons maintenant le mot-clé final lors de la définition des membres de la classe. Cela signifie que chacun de ces membres ne peut être initialisé que dans le constructeur de la classe. Ils ne peuvent pas être réaffectés ultérieurement dans une autre méthode. Nous pouvons lire ces valeurs, mais pas les changer.

Si nous créons plusieurs constructeurs pour la classe Transaction , chaque constructeur devra initialiser chaque variable finale. Ne pas le faire entraînera une erreur de compilation.

8. Conclusion

Nous avons parcouru les différentes façons dont les constructeurs construisent des objets. Lorsqu'elles sont utilisées judicieusement, les constructions forment les éléments de base de la conception orientée objet en Java.

Comme toujours, des exemples de code peuvent être trouvés sur GitHub.