1. Che cosa sono i getter e i setter
Se immagini un oggetto come una cassaforte, i suoi campi privati sono il contenuto, mentre i getter e i setter sono le chiavi dei singoli scomparti. I getter permettono di sapere che cosa c’è dentro, i setter consentono di riporre qualcosa di nuovo (ma solo se non ci metti, per esempio, un riccio al posto dei documenti).
Getter
Il getter è un metodo pubblico (public) che restituisce il valore di un campo privato (private). Di solito il suo nome inizia con get + il nome del campo con l’iniziale maiuscola.
public class Person {
private String name; // Campo privato
// Getter per il campo name
public String getName() {
return name;
}
}
Setter
Il setter è un metodo pubblico (public) che permette di modificare il valore di un campo privato. Il suo nome inizia con set + il nome del campo con l’iniziale maiuscola.
public class Person {
private String name;
// Setter per il campo name
public void setName(String name) {
this.name = name;
}
}
Per i campi boolean
Per i campi di tipo boolean è consuetudine usare il prefisso is nei getter:
private boolean active;
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
2. Sintassi e convenzioni di denominazione
Java è un linguaggio rigoroso, ma non pedante. Ci sono convenzioni consolidate che rendono il tuo codice comprensibile agli altri sviluppatori (e a te stesso tra un mese).
- Getter: public Type getNomeCampo()
- Setter: public void setNomeCampo(Type value)
- Getter per boolean: public boolean isNomeCampo()
Il nome del campo nel metodo si scrive con l’iniziale maiuscola: se il campo è age, allora i metodi saranno getAge() e setAge().
Questa convenzione segue lo stile JavaBeans, grazie al quale IDE, librerie e framework possono rilevare automaticamente i tuoi getter e setter. Per esempio, se usi Spring o JavaFX, questi metodi verranno chiamati «magicamente» quando necessario.
3. Esempi di codice
Prendiamo un progettino didattico — una semplice app di «contatti» (analoga a una rubrica telefonica) — e aggiungiamo i getter e i setter fatti bene.
Esempio: classe Contact con campi privati e getter/setter
public class Contact {
private String name;
private String phone;
private int age;
private boolean favorite;
// Getter
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getAge() {
return age;
}
public boolean isFavorite() {
return favorite;
}
// Setter
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAge(int age) {
// Esempio di validazione: l'età non deve essere negativa
if (age < 0) {
System.out.println("L'età non può essere negativa!");
return;
}
this.age = age;
}
public void setFavorite(boolean favorite) {
this.favorite = favorite;
}
}
Uso nell’applicazione
Contact friend = new Contact();
friend.setName("Ivan Ivanov");
friend.setPhone("+1-999-123-45-67");
friend.setAge(25);
friend.setFavorite(true);
System.out.println("Nome: " + friend.getName());
System.out.println("Telefono: " + friend.getPhone());
System.out.println("Età: " + friend.getAge());
System.out.println("Preferito: " + (friend.isFavorite() ? "Sì" : "No"));
Esempio di validazione nel setter
Nota che nel setter setAge abbiamo aggiunto un semplice controllo: se l’età è negativa, non modifichiamo il campo e mostriamo un avviso. È un modo semplice per proteggere l’oggetto da dati non corretti.
4. Best practices: come fare nel modo corretto
Non tutti i campi devono avere setter pubblici
A volte un campo deve essere di sola lettura — per esempio, un identificatore univoco impostato alla creazione dell’oggetto e mai più modificato. In tal caso il setter non si scrive proprio:
public class Contact {
private final int id; // final — non può essere modificato dopo l'inizializzazione
public Contact(int id) {
this.id = id;
}
public int getId() {
return id;
}
// Nessun setId!
}
Usa getter/setter per controllo accessi e validazione
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
System.out.println("Il nome non può essere vuoto!");
return;
}
this.name = name;
}
Non esporre direttamente oggetti interni mutabili
Se hai un campo — per esempio, un elenco di numeri:
private String[] phones;
Non dovresti restituirlo direttamente via getter:
public String[] getPhones() {
return phones; // Male!
}
Un tale codice permette al codice esterno di modificare l’array a piacimento — violando l’incapsulamento!
Più corretto: restituire una copia dell’array:
public String[] getPhones() {
return Arrays.copyOf(phones, phones.length); // Restituiamo una copia
}
Oppure semplicemente clonare:
public String[] getPhones() {
return phones.clone();
}
Rendi i getter e i setter chiari e semplici
- Non scrivere logica di business complessa in getter/setter — il loro compito è semplice: controllo degli accessi e, se necessario, validazione.
- Se un campo non deve cambiare — non scrivere proprio il setter.
- Se un campo non deve essere accessibile dall’esterno — non scrivere il getter.
5. Generazione automatica di getter/setter nelle IDE
Scrivere gli accessor a mano non è il massimo, soprattutto se la classe ha una decina di campi. Per fortuna, le IDE moderne (per esempio IntelliJ IDEA, Eclipse, VS Code con i plugin) sanno generarli automaticamente.
In IntelliJ IDEA
- Apri la classe, posiziona il cursore all’interno del corpo della classe.
- Premi Alt + Insert (oppure Code -> Generate...).
- Seleziona Getter and Setter.
- Spunta i campi necessari e premi OK.
Voilà! I tuoi getter e setter compariranno come per magia.
In Eclipse
- Apri la classe.
- Tasto destro — Source — Generate Getters and Setters...
- Seleziona i campi e premi OK.
In VS Code (con Java Extension Pack)
- Apri il file della classe.
- Nella Command Palette (Ctrl+Shift+P) digita Generate getters and setters.
- Segui le istruzioni.
6. Evoluzione della tua applicazione: incapsulamento in azione
Nelle lezioni precedenti hai costruito una semplice applicazione per memorizzare contatti. Ora possiamo migliorarla rendendo i campi privati e fornendo accesso solo tramite getter/setter.
Prima (cattivo esempio):
public class Contact {
public String name;
public String phone;
public int age;
}
Problema: qualsiasi codice può fare così:
Contact c = new Contact();
c.age = -1000; // Adesso abbiamo un vampiro nella rubrica!
Dopo (buon esempio):
public class Contact {
private String name;
private String phone;
private int age;
public void setAge(int age) {
if (age < 0) {
System.out.println("L'età non può essere negativa!");
return;
}
this.age = age;
}
public int getAge() {
return age;
}
// Gli altri getter/setter...
}
Ora è impossibile rompere l’oggetto dall’esterno per errore (o di proposito).
7. Getter/setter per proprietà calcolate e immutabili
A volte il valore non è memorizzato in un campo, ma viene calcolato al volo:
public class Rectangle {
private int width;
private int height;
public int getArea() {
return width * height;
}
}
Non serve un setter per l’area — non la si può impostare direttamente, si possono modificare solo larghezza o altezza.
8. Getter e setter: errori tipici
Errore n. 1: il getter/setter viola l’incapsulamento.
Se un getter restituisce un riferimento a un oggetto interno mutabile (per esempio, a una lista), il codice esterno può modificarlo aggirando tutti i controlli. Questo mina l’idea stessa di incapsulamento.
Errore n. 2: il setter non valida i dati.
Se un setter si limita ad assegnare il valore senza controllarlo, si può ottenere uno stato non valido dell’oggetto (per esempio, età negativa o nome vuoto).
Errore n. 3: generazione automatica dei setter per tutti i campi.
L’IDE può generare setter per tutti i campi, ma non è sempre corretto! Per esempio, per l’identificatore (id) il setter non serve.
Errore n. 4: logica complessa in getter/setter.
I getter e i setter devono essere semplici. Se compare logica di business complessa, è meglio estrarla in metodi separati.
Errore n. 5: violazione delle convenzioni di denominazione.
Se chiami un getter fetchName() invece di getName(), alcuni framework e librerie potrebbero non riconoscerlo.
GO TO FULL VERSION