W3docs

Costruttori Java

Usa i costruttori per inizializzare oggetti Java — costruttori default, parametrizzati e sovraccaricati, più il chaining di costruttori.

Un costruttore è un metodo speciale che viene eseguito una sola volta alla creazione di un oggetto. Il suo compito è portare la nuova istanza da "ogni campo al suo valore predefinito" a "pronta all'uso". Finora hai impostato i campi uno per uno dopo new Dog(); un costruttore ti permette di raggruppare quel lavoro nella chiamata new.

Anatomia di un costruttore

Un costruttore assomiglia a un metodo, con tre differenze: ha lo stesso nome della classe, nessun tipo di ritorno (nemmeno void), e viene chiamato solo da new:

public class Point {
  int x, y;

  public Point(int x, int y) {       // constructor
    this.x = x;
    this.y = y;
  }
}

Point p = new Point(3, 4);           // runs the constructor

La lista degli argomenti va dentro le () dopo il nome della classe nell'espressione new. Java abbina quella lista di argomenti a un costruttore della classe, proprio come abbina una chiamata a un metodo.

Il costruttore default

Se non dichiari nessun costruttore, Java fornisce alla classe un costruttore default — un costruttore senza argomenti che non fa nulla:

public class Empty { }    // compiler generates a no-arg constructor

Empty e = new Empty();    // works

È così che new Dog() funzionava negli esempi precedenti anche se non avevamo mai scritto public Dog() { ... }. Nel momento in cui dichiari qualsiasi costruttore tuo, il costruttore default gratuito scompare:

public class Point {
  int x, y;
  public Point(int x, int y) { this.x = x; this.y = y; }
}

Point p = new Point();    // ERROR — no no-arg constructor exists

Se li vuoi entrambi, dichiarali entrambi — vedi il sovraccarico qui sotto.

Perché usare un costruttore?

Due motivi.

1. Campi obbligatori. Un costruttore ti permette di rendere alcuni campi non opzionali. Con il campo pubblico e poi l'assegnazione, nulla impedisce a un chiamante di creare una Person senza name. Con un costruttore Person(String name), non è possibile.

2. Invarianti. Un costruttore può validare i propri argomenti prima che l'oggetto esista in forma utilizzabile. Se un Circle richiede un raggio positivo, il costruttore può lanciare un'eccezione su uno negativo — l'oggetto difettoso non viene mai creato.

public class Circle {
  double radius;
  public Circle(double radius) {
    if (radius <= 0) throw new IllegalArgumentException("radius must be > 0");
    this.radius = radius;
  }
}

Il chiamante ora non può ottenere un Circle con raggio non positivo.

Costruttori sovraccaricati

Una classe può avere più costruttori con liste di parametri diverse, esattamente come i metodi sovraccaricati:

public class Rectangle {
  double width, height;

  public Rectangle()                      { this(1, 1); }
  public Rectangle(double side)           { this(side, side); }    // a square
  public Rectangle(double w, double h)    { this.width = w; this.height = h; }
}

Ora tutti e tre questi compilano:

Rectangle a = new Rectangle();          // 1 x 1
Rectangle b = new Rectangle(5);         // 5 x 5
Rectangle c = new Rectangle(3, 4);      // 3 x 4

Costruttori di copia

Un sovraccarico comune è il costruttore di copia — uno che prende un'altra istanza della stessa classe e ne costruisce una copia indipendente. Java non ha un costruttore di copia integrato (a differenza di C++), quindi ne scrivi uno tuo quando ne hai bisogno:

public class Point {
  int x, y;
  public Point(int x, int y) { this.x = x; this.y = y; }
  public Point(Point other)  { this(other.x, other.y); }   // copy constructor
}

Point a = new Point(3, 4);
Point b = new Point(a);    // a separate Point with the same values

Poiché b è un nuovo oggetto, modificare b.x in seguito non influisce su a. Per le classi i cui campi sono a loro volta oggetti mutabili, copia anche quei campi (una deep copy) se vuoi che la copia sia completamente indipendente.

this(...) — chaining di costruttori

All'interno di un costruttore, this(args) chiama un altro costruttore della stessa classe. È così che l'esempio Rectangle sopra ha evitato di duplicare il codice di assegnazione dei campi: i due costruttori di convenienza delegano a quello completo.

Due regole:

  • this(...) deve essere la prima istruzione nel corpo del costruttore.
  • Un costruttore può concatenarsi a un solo altro costruttore.
public Rectangle()           { this(1, 1); }      // ok
public Rectangle(double s)   { System.out.println("hi"); this(s, s); }   // ERROR

Il motivo della regola "prima istruzione" è che la JVM deve inizializzare completamente l'oggetto esattamente una volta prima che qualsiasi altro codice venga eseguito.

super(...) — chiamare il genitore

Quando una classe estende un'altra, ogni costruttore chiama esplicitamente un costruttore del genitore con super(args), oppure chiama implicitamente il costruttore senza argomenti del genitore. Tratteremo questo argomento in modo completo nei capitoli sull'ereditarietà e sulla parola chiave super; per ora, sappi solo che super(...) esiste e ha la stessa regola della prima istruzione di this(...).

I costruttori non possono restituire un valore

Un costruttore non ha tipo di ritorno — nemmeno void. Se ne scrivi uno:

public void Point(int x, int y) { ... }    // not a constructor!

…è un metodo normale che per caso si chiama Point. Il compilatore non ti avviserà; new Point(3, 4) fallirà quindi in compilazione perché il vero costruttore che sta cercando non esiste. Questo è un errore di battitura sorprendentemente comune.

Ordine di inizializzazione

Per una singola classe, l'ordine è:

  1. I valori predefiniti dei campi vengono assegnati (ad es. i campi int diventano 0).
  2. Gli inizializzatori di campo inline (int count = 5;) e gli eventuali blocchi di inizializzazione di istanza vengono eseguiti nell'ordine in cui compaiono.
  3. Il corpo del costruttore viene eseguito.
public class Demo {
  int a = compute("a-init", 1);
  int b;

  { b = compute("b-block", 2); }    // instance initializer

  public Demo() {
    System.out.println("constructor; a=" + a + ", b=" + b);
  }

  static int compute(String label, int v) {
    System.out.println(label);
    return v;
  }
}

new Demo() stampa a-init, b-block, poi constructor; a=1, b=2.

In pratica, quasi tutta l'inizializzazione appartiene al corpo del costruttore. I blocchi di inizializzazione di istanza sono rari nel codice reale; esistono principalmente per situazioni come le classi anonime (trattate più avanti).

Un esempio pratico

java— editable, runs on the server

Cosa c'è dopo

Nei costruttori hai già visto this.field = field per disambiguare un parametro da un campo. Il capitolo sulla parola chiave this chiarisce cosa è this e i pochi posti in cui devi scriverlo esplicitamente.

Esercitazione

Pratica
Una classe dichiara un solo costruttore, public Point(int x, int y). Quale chiamata fallisce?
Una classe dichiara un solo costruttore, public Point(int x, int y). Quale chiamata fallisce?
Was this page helpful?