I costruttori

Servono a inizializzare i valori dei campi degli oggetti.

Vengono invocati automaticamente dopo che l'oggetto è stato creato.


Creazione di un oggetto

p=new Point();

Si poteva anche fare:

System.out.println(new Point());

Viene creato l'oggetto e restituito il suo riferimento (l'indirizzo di memoria in cui si trova)

Si può invocare il costruttore in ogni punto del programma in cui serve un oggetto.


Il costruttore standard

Questo tipo di costruzione mette dei valori iniziali nelle componenti:

scalari:
il valore zero
oggetti:
il valore null

Esempio di valori di default

Classe:

import java.awt.*;

class NomeClasse {
  Point p;
  int x;
}

Programma:

class ProvaCostr {
  public static void main(String args[]) {
    NomeClasse a;
    a=new NomeClasse();

    System.out.println(a.p);
    System.out.println(a.x);
  }
}

Stampa:

null
0

Alterare il costruttore standard

È possibile ridefinire il comportamento del costruttore.

import java.awt.*;

class NomeClasse {
  Point p;
  int x;

  NomeClasse() {
    this.p=new Point();
    this.p.move(10,20);

    this.x=100;
  }
}

Il programma

Uso lo stesso programma di prima:

class ProvaCostr {
  public static void main(String args[]) {
    NomeClasse a;
    a=new NomeClasse();

    System.out.println(a.p);
    System.out.println(a.x);
  }
}

Solo che ora stampa:

java.awt.Point[x=10,y=20]
100

Sintassi del costruttore

class NomeClasse {
  ...

  NomeClasse() {
    istruzioni;
  }
}

Se voglio un costruttore per la classe NewPoint, si deve chiamare NewPoint().

Il costruttore per Studente si chiama Studente(), ecc.


È un metodo statico?

Si e no.

si:
non richiede oggetto di invocazione (lo crea!)
no:
al suo interno, this è definito

Cosa succede quando si crea un oggetto

  a=new NomeClasse();

Vengono fatte due cose:

Viene poi ritornato l'indirizzo della zona di memoria creata.


Stato della memoria

  NomeClasse a;
  a=new NomeClasse();	// invocazione
classe NomeClasse {
  ...

  NomeClasse() {	// def costruttore
    istruzioni;

Dopo NomeClasse a, ma prima di invocare il costruttore:

Notare che a contiene un valore indefinito.


Quando si inizia il metodo

Quando si invoca il costruttore:

La creazione viene fatta prima dell'esecuzione delle istruzioni del costruttore.

Quando si eseguono queste istruzioni, ho già l'oggetto:


Istruzioni del costruttore

  NomeClasse() {
    this.p=new Point();
    this.p.move(10,20);

    this.x=100;
  }

this indica l'oggetto che è stato creato.

Viene modificato l'oggetto.


Valore di ritorno

Il valore di ritorno è l'indirizzo dell'oggetto che è stato appena creato.

Se ho fatto a=new NomeClasse(), questo indirizzo viene scritto in a


Riassunto

Quando faccio new NomeClasse() creo un oggetto.

Il metodo NomeClasse() della classe contiene solo le istruzioni per inizializzare l'oggetto
(quando viene invocato, l'oggetto esiste già)


A cosa serve?

Quando creo un oggetto, posso mettere nelle componenti valori diversi da 0 e null.

Più utile: costruttore con argomenti.


I costruttori di Point

Questi costruttori sono già definiti:

  p=new Point();

Crea un oggetto punto di coordinate (0,0)

  p=new Point(12,32);

Crea un oggetto punto di coordinate (12,32)


Costruttore con argomenti

Assomiglia a un metodo.

class Abcd {
  int x;
  Point p;

  Abcd(int x, Point p) {
    this.x=x;
    this.p=p;
  }
}

L'invocazione è come per i punti.

  Point p;
  p=new Point(12,32);

  Abcd o;
  o=new Abcd(12, p);

Differenza metodo-costruttore

Quando si invoca un metodo, viene fatta la copiatura dei parametri e poi si eseguono le istruzioni del metodo.

Nel caso dei costruttori, viene prima creato l'oggetto, e poi si esegue il costruttore come fosse un metodo.

C'è un passo in mezzo, in cui l'oggetto viene creato.


Di solito...

Il costruttore si usa per inizializzare le componenti degli oggetti.

Però può fare qualsiasi cosa fa un metodo.

Per esempio, può stampare una stringa:

class Abcd {
  int x;
  Point p;

  Abcd() {
    System.out.println("Questa e' una stringa");
  }
}

È sbagliato metodologicamente, ma si può fare.


Definire un nuovo costruttore

Definire un costruttore con argomenti per la classe Studente

Argomenti: i tre valori iniziali.

class Studente {
  String nome;
  int eta;
  double media;

  ...
}

Soluzione

Il nome del costruttore è uguale al nome della classe.

Non ha valore di ritorno.

Ha tre argomenti.

this indica l'oggetto che è stato appena creato.

class Studente {
  String nome;
  int eta;
  double media;

  Studente(String nome, int eta, double media) {
    this.nome=nome;
    this.eta=eta;
    this.media=media;
  }
}

Notare che il parametro formale nome si poteva anche chiamare in una altro modo, per esempio a

Non c'è relazione fra il nome del parametro formale e la componente dell'oggetto.


Esercizio

Estendere la classe Point in modo che abbia un nuovo costruttore, che prende come argomento un rettangolo, e mette nel punto il suo centro.

Nota: la nuova classe si chiama NewPoint


Soluzione

Per estendere serve un nuovo nome:

class NewPoint extends Point {
  ...
}

Si comincia con l'intestazione del metodo come al solito.

import java.awt.*;

class NewPoint extends Point {
  NewPoint(Rectangle r) {
    ...
  }
}

Definizione del costruttore

Si tratta quasi di un metodo come gli altri.

import java.awt.*;

class NewPoint extends Point {
  NewPoint(Rectangle r) {
    this.x=r.x+r.width/2;
    this.y=r.y+r.height/2;
  }
}

Calcolo, sulla base degli argomenti, i valori da mettere nelle componenti dell'oggetto appena creato.


Programma di prova

import java.awt.*;

class ProvaNewPoint {
  public static void main(String args[]) {
    Rectangle r;
    r=new Rectangle(10,10,20,30);

    NewPoint q;
    q=new NewPoint(r);

    System.out.println(q);
  }
}

I costruttori si ereditano?

I costruttori non si ereditano.

import java.awt.*;

class ProvaNewPoint {
  public static void main(String args[]) {
    NewPoint p;
    p=new NewPoint(2,4);

    NewPoint q;
    q=new NewPoint();

    System.out.println(p);
  }
}

Le due new NewPoint(...) danno errore.


Ridefinizione dei costruttori

Se voglio un costruttore, lo devo definire.

import java.awt.*;

class NewPoint extends Point {
  NewPoint() {
  }

  NewPoint(int x, int y) {
    this.x=x;
    this.y=y;
  }

  NewPoint(Rectangle r) {
    this.x=r.x+r.width/2;
    this.y=r.y+r.height/2;
  }
}

Il costruttore vuoto

Se non ci sono costruttori, si assume che ci sia quello vuoto.

Se però metto un costruttore, quello vuoto (se serve) va definito.

class Esempio1 {
  int x;
}

Si può fare new Esempio1();

class Esempio2 {
  int x;

  Esempio2(int a) {
    this.x=a;
  }

Si può fare new Esempio2(valore), ma non si può fare new Esempio2()

class Esempio3 {
  int x;

  Esempio3() {
  }

  Esempio3(int a) {
    this.x=a;
  }

Si possono fare tutte e due le cose.


Ereditare i costruttori

Se voglio solo ``ereditare'' il costruttore: definisco il costruttore con super(args) come corpo:

import java.awt.*;

class NewPoint extends Point {
  NewPoint() {
    super();
  }

  NewPoint(int x, int y) {
    super(x, y);
  }

  NewPoint(Rectangle r) {
    this.x=r.x+r.width/2;
    this.y=r.y+r.height/2;
  }
}

Di solito, conviene ridefinire il costruttore.


Il costruttore della sovraclasse

super(argomenti) invoca un costruttore della classe che è stata estesa

Quando si invoca il costruttore della superclasse, questa deve essere la prima istruzione del metodo.

import java.awt.*;

class TriPoint extends Point {
  int z;

  TriPoint() {
  }

  TriPoint(int x, int y, int z) {
    super(x, y);
    this.z=z;
  }
}

Quando un metodo non inizia con super(argomenti), si assume automaticamente super(), ossia il costruttore senza argomenti.


Costruttori sovraccarichi

Come per i metodi, posso avere costruttori con argomenti in numero o tipo diverso:

  NewPoint()
  NewPoint(int, int)
  NewPoint(Rectangle)

Esercizio

Definire i costruttori per Studente:


Soluzione

Si definiscono più costruttori.

class Studente {
  String nome;
  int eta;
  double media;

  Studente() {
  }

  Studente(String nome) {
    this.nome=nome;
  }

  Studente(String nome, int eta, double media) {
    this.nome=nome;
    this.eta=eta;
    this.media=media;
  }
}

Nota

Tutti i costruttori, se non iniziano con super(argomenti), si considerano come se iniziassero con super().

Quindi, se si definisce una classe senza il costruttore senza argomenti, allora nelle sottoclassi bisogna chiamare esplicitamente super(argomenti) come prima istruzione.

Altrimenti, lui assume super(), che non esiste.


Regoletta

Fare cosí:

Nel caso di classi estese, si può usare super
(per questo corso: non è necessario)


Argomenti dei costruttori

Di solito: gli argomenti sono i valori che vengono messi nelle componenti dell'oggetto creato (es. il costruttore di Studente qui sopra)

Qualche volta: permettono di capire i valori iniziali delle componenti (es. il rettangolo di cui si usavano i valori x e y per la classe NewPoint

In generale: il costruttore è come tutti gli altri metodi (può fare quello che vuole con gli argomenti)