En guide til konstruktører i Java

1. Introduktion

Konstruktører er portvagterne for objektorienteret design.

I denne vejledning ser vi, hvordan de fungerer som en enkelt placering, hvorfra den interne tilstand af det objekt, der oprettes, initialiseres fra.

Lad os gå videre og oprette et simpelt objekt, der repræsenterer en bankkonto.

2. Oprettelse af en bankkonto

Forestil dig, at vi skal oprette en klasse, der repræsenterer en bankkonto. Den indeholder navn, oprettelsesdato og balance.

Lad os også tilsidesætte toString metode til at udskrive detaljerne til konsollen:

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

Denne klasse indeholder nu alle de nødvendige felter, der kræves for at gemme oplysninger om en bankkonto, men den indeholder endnu ikke en konstruktør.

Dette betyder, at hvis vi opretter et nyt objekt, ville feltværdierne ikke initialiseres:

BankAccount-konto = ny BankAccount (); account.toString (); 

Kører toString ovenstående metode vil resultere i en undtagelse, fordi objekterne navn og åbnet er stadig nul:

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

3. En konstruktør uden argumenter

Lad os ordne det med en konstruktør:

klasse BankAccount {public BankAccount () {this.name = ""; this.opened = LocalDateTime.now (); dette. balance = 0,0d; }} 

Bemærk et par ting om konstruktøren, som vi lige har skrevet. For det første er det en metode, men den har ingen returtype. Det skyldes, at en konstruktør implicit returnerer typen af ​​objektet, som den opretter. Ringer ny BankAccount () nu kalder konstruktøren ovenfor.

For det andet kræver det ingen argumenter. Denne særlige type konstruktør kaldes en no-argument konstruktør.

Hvorfor havde vi dog ikke brug for det første gang? Det er fordi når vi skriv ikke eksplicit nogen konstruktør, compileren tilføjer en standard konstruktør uden argument.

Dette er grunden til, at vi var i stand til at konstruere objektet første gang, selvom vi ikke skrev en konstruktør eksplicit. Standard, ingen argumentkonstruktør indstiller simpelthen alle medlemmer til deres standardværdier.

For genstande er det nul, hvilket resulterede i den undtagelse, som vi så tidligere.

4. En parametreret konstruktør

Nu er en reel fordel ved konstruktører, at de hjælper os med at opretholde indkapsling ved injektion af tilstand i genstanden.

Så for at gøre noget virkelig nyttigt med denne bankkonto er vi nødt til at være i stand til faktisk at indsprøjte nogle indledende værdier i objektet.

At gøre det, lad os skrive en parametreret konstruktør, det vil sige en konstruktør, der tager nogle argumenter:

klasse BankAccount {public BankAccount () {...} public BankAccount (String name, LocalDateTime open, double balance) {this.name = name; dette. åbnet = åbnet; denne. balance = balance; }} 

Nu kan vi gøre noget nyttigt med vores Bankkonto klasse:

 LocalDateTime åbnet = LocalDateTime.of (2018, måned. JUNI, 29, 06, 30, 00); BankAccount-konto = ny BankAccount ("Tom", åbnet, 1000.0f); account.toString (); 

Bemærk, at vores klasse nu har 2 konstruktører. En eksplicit, ingen argumentkonstruktør og en parametreret konstruktør.

Vi kan skabe så mange konstruktører, som vi vil, men vi vil sandsynligvis ikke skabe for mange. Dette ville være lidt forvirrende.

Hvis vi finder for mange konstruktører i vores kode, kan nogle få Creational Design Patterns være nyttige.

5. En kopi-konstruktør

Konstruktører behøver ikke være begrænset til initialisering alene. De kunne også bruges til at skabe adfærd. Forestil dig, at vi skal være i stand til at oprette en ny konto fra en eksisterende konto.

Den nye konto skal have samme navn som den gamle konto, dagens oprettelsesdato og ingen midler. Vi kan gøre det ved hjælp af en kopi konstruktør:

offentlig BankAccount (anden BankAccount) {this.name = other.name; this.opened = LocalDateTime.now (); dette. balance = 0,0f; } 

Nu har vi følgende adfærd:

LocalDateTime åbnet = LocalDateTime.of (2018, måned. JUNI, 29, 06, 30, 00); BankAccount-konto = ny BankAccount ("Tim", åbnet, 1000.0f); BankAccount newAccount = ny BankAccount (konto); assertThat (account.getName ()). isEqualTo (newAccount.getName ()); assertThat (account.getOpened ()). isNotEqualTo (newAccount.getOpened ()); assertThat (newAccount.getBalance ()). er EqualTo (0.0f); 

6. En lænket konstruktør

Selvfølgelig kan vi muligvis udlede nogle af konstruktorparametrene eller give nogle af dem standardværdier.

For eksempel kunne vi bare oprette en ny bankkonto med kun navnet.

Så lad os oprette en konstruktør med en navn parameter og giv de øvrige parametre standardværdier:

offentlig BankAccount (strengnavn, LocalDateTime åbnet, dobbelt saldo) {this.name = navn; dette. åbnet = åbnet; dette. balance = balance; } public BankAccount (String name) {this (name, LocalDateTime.now (), 0.0f); }

Med nøgleordet det her, vi ringer til den anden konstruktør.

Det skal vi huske hvis vi ønsker at kæde en superklassekonstruktør, skal vi bruge super i stedet for det her.

Husk også det det her eller super udtryk skal altid være den første sætning.

7. Værdityper

En interessant brug af konstruktører i Java er i oprettelsen af Værdiobjekter. Et værdiobjekt er et objekt, der ikke ændrer sin interne tilstand efter initialisering.

Objektet er uforanderligt. Uforanderlighed i Java er lidt nuanceret, og man skal være forsigtig, når man laver objekter.

Lad os gå videre og oprette en uforanderlig klasse:

klasse Transaktion {endelig BankAccount bankAccount; endelig LocalDateTime-dato; endelig dobbeltbeløb offentlig transaktion (BankAccount-konto, LocalDateTime-dato, dobbeltbeløb) {this.bankAccount = konto; denne dato = dato; dette.beløb = beløb; }} 

Bemærk, at vi nu bruger endelig nøgleord, når man definerer klassens medlemmer. Dette betyder, at hvert af disse medlemmer kun kan initialiseres inden for klassens konstruktør. De kan ikke omfordeles senere inden i nogen anden metode. Vi kan læse disse værdier, men ikke ændre dem.

Hvis vi opretter flere konstruktører til Transaktion klasse, skal hver konstruktør initialisere hver endelige variabel. Hvis du ikke gør det, vil det resultere i en kompileringsfejl.

8. Konklusion

Vi har taget en tur gennem de forskellige måder, hvorpå konstruktører bygger genstande. Når de bruges med omtanke, udgør konstruktioner de grundlæggende byggesten i Objektorienteret design i Java.

Som altid kan kodeeksempler findes på GitHub.