Record in Java
- I record in Java sono una nuova funzionalità introdotta in JDK 16 per semplificare la creazione di classi che contengono gruppi di dati.
- I record forniscono automaticamente metodi getter, un costruttore canonico e implementazioni di
equals()
,hashCode()
e **toString()
. - I record sono immutabili, il che significa che i loro dati non possono essere modificati dopo la creazione.
- I record sono dichiarati utilizzando la parola chiave
record
e possono implementare interfacce, ma non possono estendere altre classi.
I Record
A partire da JDK 16, Java supporta una classe per scopi speciali chiamata record.
Un record è progettato per fornire un modo efficiente e facile da usare per contenere un gruppo di valori. Ad esempio, si potrebbe usare un record per contenere un insieme di coordinate; numeri di conto bancario e saldi; la lunghezza, larghezza e altezza di un container di spedizione; e così via.
Poiché contiene un gruppo di valori, un record è comunemente chiamato tipo aggregato. Tuttavia, il record è più di un semplice mezzo per raggruppare dati, perché i record hanno anche alcune delle capacità di una classe. Inoltre, un record ha caratteristiche uniche che semplificano la sua dichiarazione e ottimizzano l'accesso ai suoi valori. Di conseguenza, i record rendono molto più facile lavorare con gruppi di dati. I record sono supportati dalla nuova parola chiave sensibile al contesto record
.
Una delle motivazioni centrali per i record è la riduzione dello sforzo richiesto per creare una classe il cui scopo principale è organizzare due o più valori in una singola unità. Sebbene sia sempre stato possibile usare class
per questo scopo, farlo può comportare la scrittura di un numero di righe di codice per costruttori, metodi getter, e possibilmente (a seconda dell'uso) l'override di uno o più dei metodi ereditati da Object
.
Come vedremo, creando un aggregato di dati usando record, questi elementi sono gestiti automaticamente al nostro posto, semplificando notevolmente il codice. Un altro motivo per l'aggiunta dei record è permettere a un programma di indicare chiaramente che lo scopo previsto di una classe è contenere un raggruppamento di dati, piuttosto che agire come una classe completa.
Fondamenti dei Record
Come detto, un record è una classe specializzata e dal focus ristretto.
È dichiarato utilizzando la parola chiave context-sensitive record
. Pertanto, record
è una parola chiave solo nel contesto di una dichiarazione record
. Altrimenti, è trattata come un identificatore definito dall'utente senza significato speciale. Così, l'aggiunta di record
non impatta o rompe il codice esistente.
La forma generale di una dichiarazione record
di base è mostrata qui:
record nomeRecord(listaComponenti) {
// dichiarazioni opzionali del corpo
}
Come mostra la forma generale, una dichiarazione record
ha differenze significative da una dichiarazione class
. Prima di tutto, si noti che il nome del record è immediatamente seguito da una lista separata da virgole di dichiarazioni di parametri chiamata lista di componenti. Questa lista definisce i dati che il record conterrà.
In secondo luogo, si noti che il corpo è opzionale. Questo è reso possibile perché il compilatore fornirà automaticamente gli elementi necessari per memorizzare i dati; costruire un record; creare metodi getter per accedere ai dati; e sovrascrivere toString()
, equals()
, e hashCode()
ereditati da Object
. Di conseguenza, per molti usi di un record, non è richiesto alcun corpo perché la dichiarazione record
stessa definisce completamente il record.
Ecco un esempio di una semplice dichiarazione record:
record Impiegato(String nome, int numeroId) {}
Qui, il nome del record è Impiegato
e ha due componenti: la stringa nome
e l'intero numeroId
. Non specifica dichiarazioni nel suo corpo, quindi il suo corpo è vuoto. Come i nomi implicano, tale record potrebbe essere utilizzato per memorizzare il nome e il numero ID di un impiegato.
Data la dichiarazione Impiegato
appena mostrata, diversi elementi sono creati automaticamente. Prima di tutto, campi final
privati per nome
e numeroId
sono dichiarati rispettivamente come tipo String
e int
. In secondo luogo, metodi accessor pubblici di sola lettura (metodi getter) che hanno gli stessi nomi e tipi dei componenti del record nome
e numeroId
sono forniti. Pertanto, questi metodi getter sono chiamati nome()
e numeroId()
. In generale, ogni componente del record avrà un campo final
privato corrispondente e un metodo getter pubblico di sola lettura creato automaticamente dal compilatore.
Un altro elemento creato automaticamente dal compilatore sarà il costruttore canonico del record. Questo costruttore ha una lista di parametri che contiene gli stessi elementi, nello stesso ordine, della lista di componenti nella dichiarazione del record. I valori passati al costruttore sono automaticamente assegnati ai campi corrispondenti nel record. In un record
, il costruttore canonico prende il posto del costruttore predefinito utilizzato da una class
.
Un record
è istanziato utilizzando new
, proprio come si crea un'istanza di una class
. Per esempio, questo crea un nuovo oggetto Impiegato
, con il nome "Giovanni Rossi" e numero ID 1047:
Impiegato imp = new Impiegato("Giovanni Rossi", 1047);
Dopo che questa dichiarazione viene eseguita, i campi privati nome
e numeroId
per imp
conterranno rispettivamente i valori "Giovanni Rossi"
e 1047. Pertanto, si può utilizzare la seguente istruzione per visualizzare le informazioni associate con imp:
System.out.println("L'ID impiegato per " +
imp.nome() + " è " +
imp.numeroId());
L'output risultante è mostrato qui:
L'ID impiegato per Giovanni Rossi è 1047
Un punto chiave riguardo a un record è che i suoi dati sono conservati in campi final
privati e sono forniti solo metodi getter. Così, i dati che un record contiene sono immutabili. In altre parole, una volta che si costruisce un record, i suoi contenuti non possono essere cambiati. Tuttavia, se un record contiene un riferimento a qualche oggetto, si può fare un cambiamento a quell'oggetto, ma non si può cambiare a quale oggetto il riferimento nel record si riferisce. Così, in termini Java, i record sono detti essere superficialmente immutabili.
Il seguente programma mette in azione la discussione precedente. I record sono spesso utilizzati come elementi in una lista. Per esempio, un'azienda potrebbe mantenere una lista di record Impiegato
per memorizzare il nome di un impiegato insieme al suo numero ID corrispondente. Il seguente programma mostra un semplice esempio di tale utilizzo. Crea un piccolo array di record Impiegato
. Poi cicla attraverso l'array, visualizzando i contenuti di ogni record.
// Un semplice esempio di Record.
// Dichiara un record impiegato. Questo crea automaticamente una
// classe record con campi privati, final chiamati nome e numeroId,
// e con accessor di sola lettura chiamati nome() e numeroId().
record Impiegato(String nome, int numeroId) {
}
class DemoRecord {
public static void main(String[] args) {
// Crea un array di record Impiegato.
Impiegato[] listaImp = new Impiegato[4];
// Crea una lista di impiegati che usa il record Impiegato.
// Notate come ogni record è costruito. Gli argomenti
// sono automaticamente assegnati ai campi nome e numeroId nel
// record che sta essere creato.
listaImp[0] = new Impiegato("Giovanni Rossi", 1047);
listaImp[1] = new Impiegato("Michele Verdi", 1048);
listaImp[2] = new Impiegato("Serena Bianchi", 1049);
listaImp[3] = new Impiegato("Silvia Neri", 1050);
// Usa gli accessor del record per visualizzare nomi e ID.
for (Impiegato e : listaImp)
System.out.println("L'ID impiegato per " + e.nome() + " è " +
e.numeroId());
}
}
L'output è mostrato qui:
L'ID impiegato per Giovanni Rossi è 1047
L'ID impiegato per Michele Verdi è 1048
L'ID impiegato per Serena Bianchi è 1049
L'ID impiegato per Silvia Neri è 1050
Prima di continuare, è importante menzionare alcuni punti chiave relativi ai record. Prima di tutto, un record non può ereditare un'altra classe. Tuttavia, tutti i record ereditano implicitamente java.lang.Record
, che specifica sovrascritture astratte dei metodi equals()
, hashCode()
, e toString()
dichiarati da Object
.
Implementazioni implicite di questi metodi sono create automaticamente, basate sulla dichiarazione del record. Un tipo record
non può essere esteso. Così, tutte le dichiarazioni record
sono considerate final
. Anche se un record
non può estendere un'altra classe, può implementare una o più interfacce. Ad eccezione di equals
, non si possono utilizzare i nomi dei metodi definiti da Object
come nomi per i componenti di un record
. A parte i campi associati con i componenti di un record
, qualsiasi altro campo deve essere static
. Infine, un record
può essere generico.