Un guide pour les constructeurs en Java

Guide des constructeurs en Java

1. introduction

Les constructeurs sont les gardiens deobject-oriented design.

Dans ce tutoriel, nous allons voir comment ils agissent comme un emplacement unique à partir duquel initialiserthe internal state 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. Configurer un compte bancaire

Imaginons 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.

Remplaçons également la méthodetoString pour imprimer les détails sur 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 de champ ne seront pas initialisées:

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

L'exécution de la méthodetoString ci-dessus entraînera une exception car les objetsname etopened sont toujoursnull:

java.lang.NullPointerException
    at com.example.constructors.BankAccount.toString(BankAccount.java:12)
    at com.example.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;
    }
}

Notez quelques petites choses sur le constructeur que nous venons d’écrire. Tout d'abord, il s'agit d'une méthode, mais elle n'a pas de type de retour. C'est parce qu'un constructeur renvoie implicitement le type de l'objet qu'il crée. Appeler new BankAccount() maintenant appellera le constructeur ci-dessus.

Deuxièmement, cela ne nécessite aucun argument. Ce type particulier de constructeur est appelé un no-argument constructor.

Pourquoi n’en avons-nous pas besoin pour la première fois? C’est parce que lorsque nousdon’t explicitly write any constructor, the compiler adds a default, no-argument constructor.

C'est pourquoi nous avons pu construire l'objet la première fois, même si nous n'avons pas écrit de constructeur explicitement. La valeur par défaut, aucun constructeur d'argument ne définira simplement tous les membres sur leursdefault values.

Pour les objets, c'estnull, 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 à maintenirencapsulation lors de l'injection d'état dans l'objet.

Donc, pour faire quelque chose de vraiment utile avec ce compte bancaire, nous devons être capables d’injecter certaines valeurs initiales dans l’objet.

Pour ce faire,let’s write a parameterized constructor, that is, a constructor that takes some arguments:

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

Maintenant, nous pouvons faire quelque chose d'utile avec notre classeBankAccount:

    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, quelquesCreational Design Patterns pourraient être utiles.

5. Un constructeur de copie

Les constructeurs ne doivent pas nécessairement se limiter à l'initialisation. Ils pourraient également être utilisés pour créer des comportements. Imagine that we need to be able to create a new account from an existing one.

Le nouveau compte doit porter le même nom que l'ancien compte, la date de création d'aujourd'hui et aucun fonds. We can do that using a copy constructor:

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 pouvons être en mesure de déduire certains des paramètres du constructeur ougive some of them default values.

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

Alors, créons un constructeur avec un paramètrename 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 queif we want to chain a superclass constructor we have to use super instead of this.

Souvenez-vous également quethis or super expression should always be the first statement.

7. Types de valeur

Une utilisation intéressante des constructeurs en Java est la création deValue Objects. A value object is an object that does not change its internal state after initialization.

That is, the object is immutable. L'immuabilité en Java est un peunuanced 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.

If we create multiple constructors for the Transaction class, each constructor will need to initialize every final variable. Ne pas le faire entraînera une erreur de compilation.

8. Conclusion

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

Comme toujours, des exemples de code peuvent être trouvésover on GitHub.