Lezione 7 · Canale 2 · mercoledì 7 ottobre 2026
Classi Java: incapsulamento e costruttori
Progettazione del Software
Riassunto
Le classi Java raccolgono proprietà in campi e funzionalità in metodi. La distinzione tra membri public e private implementa il principio di information hiding: l'oggetto nasconde come lavora internamente e offre un'interfaccia ai client. Getter e setter controllano l'accesso alle proprietà; una proprietà senza setter viene inizializzata tramite il costruttore. Proprietà mutabili possono cambiare durante la vita dell'oggetto; proprietà immutabili restano fisse. L'overloading consente di definire metodi con lo stesso nome ma parametri diversi. this identifica l'oggetto corrente. Gli esempi di Persona, Automobile, Punto e Segmento mostrano come progettare classi, creare oggetti e modellare concetti del mondo reale.
Concetti chiave
- Classe — definizione che raccoglie proprietà, rappresentate da campi, e metodi che descrivono le funzionalità offerte.
- Proprietà — informazione scelta per caratterizzare gli oggetti e le istanze di una classe.
- Campo o variabile di istanza — variabile che rappresenta una proprietà all'interno dell'oggetto.
- Information hiding — principio per cui l'oggetto nasconde all'esterno come lavora e come rappresenta internamente i propri dati, offrendo operazioni attraverso la propria interfaccia.
public— modificatore che rende un campo o un metodo visibile ai potenziali client della classe.private— modificatore che limita l'accesso a un campo o metodo al codice della stessa classe.- Getter — metodo che permette a un client di chiedere il valore di una proprietà tramite l'interfaccia della classe.
- Setter — metodo che modifica una proprietà dell'oggetto; tipicamente ha tipo di ritorno
voide riceve un parametro del tipo della proprietà esposta. - Proprietà mutabile — proprietà che può cambiare durante il ciclo di vita dell'oggetto; in genere è accessibile con getter e setter.
- Proprietà immutabile — proprietà che non cambia durante il ciclo di vita dell'oggetto; in genere ha un getter ed è inizializzata tramite un costruttore.
- Interfaccia della classe — insieme dei campi e dei metodi
publicche dichiarano come i client possono interagire con la classe. - Metodo helper — metodo
privateusato per supportare l'implementazione interna della classe, senza essere esposto ai client. - Costruttore — funzione speciale con lo stesso nome della classe e senza tipo di ritorno, usata per inizializzare l'oggetto e invocata da
new. - Overloading — possibilità di definire metodi o costruttori con lo stesso nome, distinguendoli per numero e tipo dei parametri; il tipo di ritorno non basta a distinguerli.
- Oggetto — entità caratterizzata da dati e comportamento, creata come istanza di una classe.
this— riferimento all'oggetto su cui è stato invocato il metodo.- Metodo statico — metodo associato alla classe e non a uno specifico oggetto; nell'esempio è usato per rappresentare servizi rivolti ai client.
- Omogeneità e coesione — caratteristiche per cui i dati relativi a un concetto e i comportamenti che operano su di essi vengono raccolti nel modulo della classe.
Sviluppo
Proprietà e metodi di una classe
Una classe è caratterizzata da proprietà, dette anche attributi, e da metodi, che rappresentano le funzionalità. Le proprietà contengono le informazioni scelte per caratterizzare ogni oggetto della classe. In Java possono essere dichiarate public oppure private.
Information hiding: esempio della classe Persona
La classe Persona viene introdotta con due proprietà di tipo stringa: nome e residenza. I servizi ipotizzati sono getNome, getResidenza e setResidenza. L'oggetto offre operazioni a cui gli altri oggetti possono rivolgersi: il client chiede un servizio e l'oggetto risponde eseguendo il metodo invocato su di esso.
Il principio di information hiding richiede che il client usi i servizi esposti senza dipendere da come i dati sono memorizzati. Per esempio, il nome potrebbe essere rappresentato internamente in modi diversi, mentre il servizio getNome restituisce al client il valore attraverso l'interfaccia prevista.
Campi privati e scelta dei servizi
Il campo nome viene dichiarato privato: il client non può leggerlo o modificarlo direttamente. Può chiedere il nome tramite getNome, che ha la responsabilità di restituire il valore nel formato previsto dall'interfaccia.
La classe non espone setNome. La scelta riflette il progetto: il nome viene considerato una caratteristica identificativa che non si vuole poter cambiare durante la vita dell'oggetto. Il client non deve poter accedere direttamente alla cella di memoria del campo nell'oggetto.
Getter, setter e mutabilità
In generale, per rappresentare una proprietà si usano campi privati e si decide quali metodi get e set esporre. Un getter è normalmente previsto per rendere consultabile la proprietà; il setter si aggiunge quando è coerente consentirne la modifica.
La residenza, anch'essa rappresentata come stringa nell'esempio, può essere consultata con getResidenza e modificata con setResidenza. Una persona può cambiare residenza nel tempo, quindi questa proprietà è mutabile. Il setter agisce sull'oggetto su cui è invocato e, in genere, riceve un parametro del tipo usato dal getter per esporre la proprietà.
Proprietà immutabili e costruttori
Il setter produce un side effect sull'oggetto di invocazione, modificandone una proprietà. Per una proprietà senza setter, come il nome nell'esempio, l'inizializzazione avverrà quando si crea l'oggetto, tramite un costruttore.
La progettazione distingue quindi proprietà mutabili, che tipicamente hanno getter e setter, e proprietà immutabili, che hanno getter ma vengono inizializzate dai costruttori. La scelta dipende dall'applicazione; le modalità di implementazione in Java seguono gli stessi meccanismi del linguaggio.
Visibilità e metodi helper
I campi e i metodi public sono visibili ai potenziali client. Un campo o metodo private non è visibile dall'esterno; un metodo privato può essere chiamato soltanto dal codice della stessa classe. Questi metodi ausiliari sono detti metodi helper e supportano l'implementazione interna.
Uso di Persona da parte di un client
Un client dichiara un riferimento di tipo Persona e crea un oggetto con new Persona(). Può poi modificare la residenza usando il setter e leggerla attraverso il getter, per esempio stampando il risultato di getResidenza().
Un tentativo di accedere direttamente a un campo privato, come P1.residenza, provoca un errore di compilazione: il codice client non si trova all'interno della classe Persona e non può accedere a quel campo.
Information hiding, interfaccia e confronto con C
La programmazione orientata agli oggetti offre moduli con information hiding, interfacciamento esplicito, modello client-server, omogeneità e coesione. Il client può eseguire solo le operazioni dichiarate dall'interfaccia; il progettista sceglie quali servizi rendere disponibili e nasconde la rappresentazione interna.
Nel confronto con il linguaggio C, le procedure che elaborano i dati sono definite separatamente dalle strutture che li contengono. Il legame tra struttura e procedure dipende dal progetto seguito dal programmatore. In Java la classe raccoglie dati e metodi che operano sull'oggetto, rendendo quel legame supportato dal linguaggio. Questo modello è più astratto e vicino al modo di rappresentare concetti del mondo reale.
Interfaccia della classe e overloading
I servizi destinati ai client vengono dichiarati public; i campi che rappresentano le proprietà sono normalmente private, così come le eventuali funzioni ausiliarie. L'insieme dei membri pubblici costituisce l'interfaccia della classe.
L'overloading permette di definire più metodi con lo stesso nome. Il compilatore li distingue dal numero e dal tipo dei parametri, non dal tipo di ritorno. Perciò due metodi f senza parametri non possono essere distinti solo perché uno restituisce void e l'altro un intero. L'overloading ha un ruolo importante nel polimorfismo trattato con l'ereditarietà.
Costruttore e creazione dell'oggetto
Il costruttore serve a inizializzare i campi privati per i quali non si espone un setter. Ha lo stesso nome della classe e non dichiara alcun tipo di ritorno: anche void sarebbe errato. Nell'esempio viene dichiarato public, così i client possono creare oggetti della classe.
La creazione viene avviata con new, che alloca la memoria dell'oggetto; new invoca poi il costruttore per inizializzarne i campi. Il costruttore opera sull'oggetto appena allocato, prima che new restituisca al client il riferimento.
Sequenza di allocazione e inizializzazione
Nel modello descritto, la variabile di riferimento del client si trova nello stack e l'oggetto viene creato nell'heap. Dopo l'allocazione, i campi hanno i valori predefiniti previsti dalla JVM. Il costruttore scrive i valori iniziali nell'oggetto; infine new restituisce il riferimento, che viene assegnato alla variabile del client.
Per esempio, una chiamata a un costruttore di Persona può ricevere nome e residenza. Una volta creato l'oggetto, il client può usare i getter e, se la residenza è mutabile, il relativo setter.
Valori predefiniti e overloading dei costruttori
Se il costruttore non inizializza un campo, questo conserva il valore predefinito assegnato dalla JVM: 0 per gli interi, 0.0 per i tipi floating point, false per i booleani, il carattere nullo per char e null per i campi di tipo riferimento, come stringhe o oggetti.
Anche i costruttori possono essere overloaded: si distinguono in base al numero e al tipo dei parametri. new seleziona il costruttore compatibile con gli argomenti forniti. La classe può quindi offrire, per esempio, un costruttore con il solo nome e uno con nome e residenza.
Costruttore senza parametri
Se non viene dichiarato alcun costruttore, Java fornisce un costruttore predefinito senza parametri, che non aggiunge inizializzazioni oltre ai valori assegnati dalla JVM. Quando si dichiara un qualsiasi costruttore, quello predefinito automatico non viene più fornito.
Se serve ancora creare l'oggetto senza argomenti, occorre dichiarare esplicitamente un costruttore senza parametri. Il progettista decide se consentirlo: può essere utile assegnare valori predefiniti, ma non è sempre appropriato per la classe.
Progettazione completa di Persona
La classe Persona esemplificata raccoglie i campi privati nome e residenza, un costruttore senza parametri che assegna valori predefiniti, un costruttore che inizializza il nome e uno che inizializza nome e residenza. Espone inoltre i getter per entrambe le proprietà e il setter per la residenza. Il nome resta senza setter, coerentemente con la scelta di trattarlo come immutabile.
Analisi e progettazione della classe Automobile
Per progettare una classe che rappresenti un'automobile, prima si individuano le informazioni rilevanti per l'applicazione. Nell'esercizio sono targa, modello, colore e nome del proprietario. Targa e modello sono considerate immutabili; colore e proprietario sono modificabili.
Il progettista sceglie poi come rappresentare i campi, quali funzionalità offrire e quali membri rendere pubblici. La progettazione e l'interfaccia devono essere definite prima di implementare i metodi, così da non modificare la struttura della classe in modo improvvisato mentre si scrive il codice.
Campi, costruttori e sviluppo iterativo
Nell'esempio i campi privati targa, modello e colore sono stringhe, mentre proprietario è un riferimento a un oggetto Persona. La rappresentazione è semplificata per concentrarsi sulla programmazione a oggetti. L'automobile e il suo proprietario sono oggetti distinti, collegati dal riferimento contenuto nel campo proprietario.
Si prevede un costruttore che inizializza targa, modello e colore; può essere utile anche un costruttore che riceva il proprietario, per esempio per automobili realizzate su commessa. Il colore viene inizializzato alla creazione anche se è mutabile, perché si assume che un'automobile nasca già verniciata. Un'automobile in salone può invece non avere ancora un proprietario.
Durante la scrittura del codice è utile compilare frequentemente: aggiungere poche parti per volta aiuta a ricondurre gli errori al codice appena inserito. Questa pratica è detta sviluppo iterativo, in contrapposizione allo sviluppo batch di molto codice prima della compilazione. Commenti e nomi espliciti migliorano inoltre la leggibilità e la manutenzione.
Metodi pubblici di Automobile
L'interfaccia dell'automobile comprende getter per targa, modello, colore e proprietario, oltre ai setter per colore e proprietario. Il getter del proprietario restituisce un oggetto Persona; il setter riceve un nuovo proprietario dello stesso tipo. In questo esempio non sono necessari metodi helper.
Servizi statici e client
Un client può esporre un servizio di verniciatura che riceve un'automobile e un colore, quindi chiama setColore sull'oggetto ricevuto. Il cambiamento rappresenta nel sistema informativo l'evento di verniciatura; il codice non esegue materialmente il lavoro sulla carrozzeria.
La classe ServiziAuto raccoglie servizi statici, non associati a una specifica automobile. Il metodo di verniciatura è un esempio di servizio per un client. Un altro metodo può creare un'automobile con la targa ricevuta, il modello Ferrari 360 GT e il colore rosso Maranello. Le classi che raccolgono queste funzionalità sono moduli funzionali; le classi con metodi statici possono rappresentare casi d'uso, cioè client del codice.
main, oggetti e avvio della computazione
Il main avvia una sequenza di operazioni: crea un'automobile, crea una persona e la assegna come proprietaria, usa il servizio per cambiare il colore e stampa i dati tramite getter. Poi crea una seconda automobile, un'altra persona e assegna anche il secondo proprietario. Le stampe servono a seguire gli effetti delle operazioni. Nella classe del main si definiscono due metodi helper statici: uno stampa i dati di un'automobile tramite i getter, l'altro quelli del proprietario.
Nel programma si distinguono le classi dagli oggetti: le classi sono definizioni del codice; nell'esempio le classi sono main, Persona, Automobile e ServiziAuto. Gli oggetti creati e ancora presenti sono invece due automobili e due persone, per un totale di quattro. Cambiare il colore della prima automobile modifica l'oggetto esistente e non ne crea uno nuovo.
Il main dà avvio alla computazione creando oggetti e chiamando metodi. La computazione prosegue tramite invocazioni che possono coinvolgere altri oggetti e i loro servizi.
Riferimento this
this indica l'oggetto su cui è stato invocato il metodo, cioè l'oggetto corrente. Può essere usato per rendere esplicito che un campo appartiene all'oggetto. Per esempio, in un costruttore con parametri chiamati come i campi, this.targa identifica il campo e targa il parametro.
Il suo uso può migliorare la leggibilità e risolve l'ambiguità tra il nome di un campo e quello di un parametro. Un metodo statico non può usare this, perché non è associato a un oggetto specifico.
Classi Punto e Segmento
Un punto nello spazio viene rappresentato con tre campi privati double, per le coordinate x, y e z. Le coordinate sono considerate immutabili: la classe offre un costruttore con tre argomenti e getter. Si può inoltre dichiarare esplicitamente un costruttore senza parametri, che lascia le coordinate ai valori predefiniti della JVM e rappresenta l'origine.
Un segmento è rappresentato da un punto di inizio e uno di fine. Il costruttore può ricevere entrambi i punti oppure soltanto quello finale: nel secondo caso l'inizio viene impostato all'origine tramite un nuovo oggetto Punto. Getter per inizio e fine consentono di consultare gli estremi. Questo modello illustra come rappresentare concetti geometrici con oggetti e riferimenti tra oggetti.