Una guida ai costruttori in Java

1. Introduzione

I costruttori sono i custodi del design orientato agli oggetti .

In questo tutorial vedremo come agiscono come un'unica posizione da cui inizializzare lo stato interno dell'oggetto creato.

Andiamo avanti e creiamo un semplice oggetto che rappresenti un conto bancario.

2. Creazione di un conto bancario

Immagina di dover creare una classe che rappresenti un conto bancario. Conterrà un nome, una data di creazione e un saldo.

Inoltre, sovrascriviamo il metodo toString per stampare i dettagli sulla 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); } } 

Ora, questa classe contiene tutti i campi necessari richiesti per memorizzare le informazioni su un conto bancario, ma non contiene ancora un costruttore.

Ciò significa che se creiamo un nuovo oggetto, i valori del campo non verranno inizializzati:

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

L'esecuzione del metodo toString sopra risulterà in un'eccezione perché il nome degli oggetti e aperti sono ancora nulli :

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 costruttore senza argomenti

Risolviamolo con un costruttore:

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

Notare alcune cose sul costruttore che abbiamo appena scritto. Innanzitutto, è un metodo, ma non ha un tipo di ritorno. Questo perché un costruttore restituisce implicitamente il tipo di oggetto che crea. Chiamare new BankAccount () ora chiamerà il costruttore sopra.

In secondo luogo, non ci vogliono argomenti. Questo particolare tipo di costruttore è chiamato costruttore o-argomento .

Perché non ne abbiamo avuto bisogno per la prima volta, però? È perché quando non scriviamo esplicitamente alcun costruttore, il compilatore aggiunge un costruttore predefinito senza argomenti .

Questo è il motivo per cui siamo stati in grado di costruire l'oggetto la prima volta, anche se non abbiamo scritto un costruttore in modo esplicito. Il costruttore predefinito, senza argomenti, imposterà semplicemente tutti i membri sui valori predefiniti.

Per gli oggetti, è nullo, il che ha generato l'eccezione che abbiamo visto in precedenza.

4. Un costruttore parametrizzato

Ora, un vero vantaggio dei costruttori è che ci aiutano a mantenere l' incapsulamento quando iniettano lo stato nell'oggetto.

Quindi, per fare qualcosa di veramente utile con questo conto bancario, dobbiamo essere in grado di iniettare effettivamente alcuni valori iniziali nell'oggetto.

Per fare ciò, scriviamo un costruttore parametrizzato , cioè un costruttore che accetta alcuni argomenti :

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

Ora possiamo fare qualcosa di utile con la nostra classe BankAccount :

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

Si noti che la nostra classe ora ha 2 costruttori. Un costruttore esplicito, senza argomenti e un costruttore parametrizzato.

Possiamo creare tutti i costruttori che vogliamo, ma probabilmente non vorremmo crearne troppi. Ciò creerebbe un po 'di confusione.

Se troviamo troppi costruttori nel nostro codice, alcuni Creational Design Patterns potrebbero essere utili.

5. Un costruttore di copie

I costruttori non devono essere limitati alla sola inizializzazione. Potrebbero anche essere usati per creare comportamenti. Immagina di dover essere in grado di creare un nuovo account da uno esistente.

Il nuovo account dovrebbe avere lo stesso nome del vecchio account, la data di creazione odierna e nessun fondo. Possiamo farlo usando un costruttore di copie :

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

Ora abbiamo il seguente comportamento:

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 costruttore incatenato

Naturalmente, potremmo essere in grado di dedurre alcuni dei parametri del costruttore o dare ad alcuni di essi valori predefiniti.

Ad esempio, potremmo semplicemente creare un nuovo conto bancario con solo il nome.

Quindi, creiamo un costruttore con un parametro name e diamo agli altri parametri valori predefiniti:

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); }

Con la parola chiave this, chiamiamo l'altro costruttore.

Dobbiamo ricordare che se vogliamo concatenare un costruttore di superclassi dobbiamo usare super invece di questo .

Inoltre, ricorda che questa o la super espressione dovrebbe sempre essere la prima affermazione.

7. Tipi di valore

Un uso interessante dei costruttori in Java è nella creazione di oggetti valore . Un oggetto valore è un oggetto che non cambia il proprio stato interno dopo l'inizializzazione.

Cioè, l'oggetto è immutabile . L'immutabilità in Java è un po 'sfumata e bisogna fare attenzione quando si creano oggetti.

Andiamo avanti e creiamo una classe immutabile:

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; } } 

Notate che ora usiamo la parola chiave final quando definiamo i membri della classe. Ciò significa che ciascuno di questi membri può essere inizializzato solo all'interno del costruttore della classe. Non possono essere riassegnati successivamente all'interno di qualsiasi altro metodo. Possiamo leggere quei valori, ma non cambiarli.

Se creiamo più costruttori per la classe Transaction , ogni costruttore dovrà inizializzare ogni variabile finale. In caso contrario, verrà generato un errore di compilazione.

8. Conclusione

Abbiamo esaminato i diversi modi in cui i costruttori creano oggetti. Se usati con giudizio, i costrutti costituiscono i mattoni di base della progettazione orientata agli oggetti in Java.

Come sempre, è possibile trovare esempi di codice su GitHub.