Una classe di dati è un concetto non è legato a nessun linguaggio di programmazione specifico, si tratta di un modello abbastanza conveniente per la maggior parte dei programmatori come un modo semplice per rappresentare, incapsulare e spostare le informazioni.

Una classe di dati si riferisce a una classe che contiene solo campi e metodi crud per accedervi (getter e setter). Questi sono semplicemente contenitori per i dati utilizzati da altre classi. Queste classi non contengono alcuna funzionalità aggiuntiva e non possono operare in modo indipendente sui dati di loro proprietà.

Di solito, le classi di dati rappresentano entità del mondo reale ed è comune avere dozzine o centinaia di queste classi in un progetto, il che significa che creare, modificare e manipolare questi oggetti è un compito molto comune per uno sviluppatore.

Una classe di dati in Java

Questo è il modo in cui una classe di dati di solito appare in Java:

Noterai che stiamo sovrascrivendo i metodi toString() , equals() e hashCode() (dichiarati nella classe Object.java). Perché l’override di questi metodi è rilevante nelle classi di dati?

Quando implementiamo questi tre metodi creiamo oggetti di valore, classi per le quali due istanze qualsiasi con valori di campo opportunamente uguali sono considerate intercambiabili. (Nota: per essere completamente vero, avremo bisogno di rendere la classe immutabile. Rendere i campi definitivi e rimuovere i setter aiuta. L’immutabilità è spesso considerata una buona pratica e raccomandata quando possibile)

  • equals() : Per impostazione predefinita, restituisce true solo se le due variabili si riferiscono allo stesso oggetto, cioè se la posizione in memoria è la stessa. Sovrascriviamo questo metodo per restituire true se gli oggetti contengono le stesse informazioni, in altre parole, se gli oggetti rappresentano la stessa entità. Controlla che le proprietà siano le stesse e contengano lo stesso valore.
  • hashCode() : Per impostazione predefinita, restituisce l’indirizzo di memoria dell’oggetto in esadecimale. È un valore numerico per identificare un oggetto durante il test di uguaglianza, ovvero oggetti uguali hanno lo stesso codice hash. Questo metodo deve essere sovrascritto quando toString() viene sovrascritto in modo da restituire un codice hash calcolato dai valori delle proprietà.
  • toString(): Per impostazione predefinita restituisce il tipo di oggetto e hashCode(), ad esempio [email protected]+. Sovrascriviamo questo metodo per avere una versione più leggibile dell’oggetto come User(name=Steve, surname=Jobs).

Nonostante sia un concetto così importante, non c’è nulla nel codice Java sopra che renda questa classe diversa da qualsiasi altra. I programmatori possono riconoscerlo come una classe di dati a causa della struttura della classe e dei modelli, ma dal punto di vista del compilatore, questa classe è solo un’altra classe.

La creazione di classi di dati è così comune che gli sviluppatori usano spesso l’IDE e altri plugin per aiutarli in questa attività ripetitiva. Il dolore di creare una classe di dati in Java può essere alleviato dai plugin o dall’IDE, ma la maggior parte dei bug viene introdotta su ulteriori modifiche di tali classi. È molto facile dimenticare di modificare tutti i metodi di accompagnamento di conseguenza ogni volta che un campo viene rimosso o aggiunto.

Una classe di dati in Java manca del supporto per la lingua. È un compito ripetitivo e soggetto a bug che rappresenta troppo attrito per un linguaggio di programmazione moderno.

Una classe di dati in Kotlin

La stessa classe di dati in Kotlin sarebbe simile a questa:

data class User(var name: String, var age: Int)

Kotlin eleva le classi di dati ai cittadini di prima classe introducendo la parola chiave data. Scomponiamolo.

  • Getter e setter

I getter e setter vengono creati automaticamente in Kotlin quando dichiariamo le proprietà. In breve ,ciò che var name: String significa è che la classe User ha una proprietà pubblica (visibilità predefinita in Kotlin), mutabile (var) ed è un tipo String. Dato che è pubblico crea il getter, e dato che è mutabile crea il setter.

Se vogliamo rendere la classe di sola lettura (senza setter), dobbiamo usare val :

data class User(val name: String, val age: Int)

Possiamo mescolare val e var nella stessa dichiarazione di classe. Puoi pensare a val come a final variabile in Java.

Tutto spiegato finora, è comune a qualsiasi dichiarazione di classe in Kotlin, ma la parola chiave data è ciò che sta facendo la differenza qui.

  • La parola chiave data

Che dichiara una classe come classe di dati ci farà implementare automaticamente toString(), hashCode() e equals() nello stesso modo descritto sopra per la classe Java. Quindi se creiamo una classe utente come User("Steve Jobs",56) e chiamiamo il metodo toString() otterremo qualcosa come:User(name=Steve Jobs, age=56).

Destrutturazione delle dichiarazioni

La parola chiave data fornisce funzioni che consentono la destrutturazione delle dichiarazioni. In breve, crea una funzione per ogni proprietà in modo che possiamo fare cose come questa:

Funzione di copia

La parola chiave data ci offre un modo pratico di copiare le classi modificando il valore di alcune proprietà. Se vogliamo creare una copia di un utente che cambia l’età, questo è il modo in cui lo faremmo:

Le proprietà dichiarate nel corpo della classe vengono ignorate

Il compilatore utilizza solo le proprietà definite all’interno del costruttore primario per le funzioni generate automaticamente.

La proprietà address non verrà trattata dalla parola chiave data, quindi significa che le implementazioni generate automaticamente la ignoreranno.

Pair e Triple

Pair e Triple sono classi di dati standard nella libreria, ma i documenti Kotin stessi scoraggiano l’uso di questi a favore di classi di dati più leggibili e personalizzate.

Requisiti e limitazioni

  • Un costruttore di classi di dati deve avere almeno un parametro.
  • Tutti i parametri devono essere di mercato come val o var.
  • Una classe di dati non può essere abstract, open, sealed o inner.
  • equals , toString e i metodi hashCode possono essere sovrascritti esplicitamente.
  • Le implementazioni esplicite per le funzioni componentN() e copy() non sono consentite.
  • La derivazione di una classe di dati da un tipo con una firma corrispondente alla funzione copy() è stata deprecata in Kotlin 1.2 ed è stata vietata in Kotlin 1.3.
  • Una classe data non può estendersi da un’altra classe data.
  • Una classe data può estendere altre classi (da Kotlin 1.1)

Le classi di dati sono cittadini di prima classe a Kotlin. In una sintassi molto breve offrono una soluzione senza attrito con tutti i vantaggi e senza compromessi.

Cosa significa per Android

Cercherò di spiegare quali sono i principali vantaggi che ho trovato usando le classi di dati Kotlin nei miei progetti Android. Questi non sono tutti e potrebbero non essere i più importanti, ma questi sono i più evidenti dalla mia esperienza finora.

  • Modelli di dati

L’architettura in Android era (ed è ancora) un argomento caldo. Ci sono molteplici opzioni, ma la maggior parte di loro hanno in comune la separazione delle preoccupazioni e principi di responsabilità unica. Clean Architecture – molto famoso nella comunità Android-è un insieme di buone pratiche da seguire quando si mira a una buona architettura che pone l’accento sull’utilizzo di modelli diversi per diversi strati dell’architettura. Ciò significa che un progetto con 3 o 4 diversi modelli di dati diventano standard.

Ciò significa che il numero di classi di dati in un progetto è molto alto e operazioni come creare, leggere, modificare, copiare, confrontare, mappare classes le classi del modello di dati sono attività quotidiane che beneficiano del codice generato automaticamente dalla classe di dati Kotin. Consente di risparmiare un’enorme quantità di tempo e riduce le opportunità di introdurre bug.

  • Non più Auto-Value

L’utilizzo dei tipi di valore è una buona pratica molto estesa in Android, specialmente tra le classi di dati.

Un tipo di valore è un oggetto da una classe di valore immutabile.
Una classe di valore è una classe per la quale l’uguaglianza dipende dal suo contenuto.
Una classe immutabile è una classe che non può essere modificata dopo la creazione.

AutoValue è una libreria popolare di Google che ci aiuta a creare tipi di valore. Fa il suo lavoro ma è molto prolisso per qualcosa che non dovrebbe essere così complicato.

Usando le classi kotlin data con il modificatore di accesso val ci dà un’approssimazione abbastanza vicina ai tipi di valore.

data class User(val name : String, val age : Int)

La classe Utente precedente è una classe abbastanza vicina ai tipi di valore, in una sintassi molto più breve. Per una buona fetta di persone, AutoValue può essere sostituito da Kotlin. Meno codice, nessuna elaborazione di annotazioni e una libreria in meno da cui dipendere.

Nota: Kotlin offre proprietà e classi di sola lettura con la parola chiave val. Sola lettura e immutabile non è la stessa (più qui), ma in generale, è considerato abbastanza buono per scopi pratici.

  • Non più Lombok

Uno dei modi in cui gli sviluppatori hanno cercato di risparmiare tempo quando si trattava di classi di dati è l’utilizzo di librerie per generare metodi getter e setter. Lombok è uno dei (in)famosi in Android / Java. Richiede non solo la libreria, ma anche un plugin per AS. La lunga storia breve è per la maggior parte degli sviluppatori Lombok portare tanti vantaggi come mal di testa, così diventa quella libreria si inizia ad amare, ma dopo un po’, non vedo l’ora di sbarazzarsi di, ma non si fa mai perché è ovunque.

Dato che le classi di dati Kotlin non richiedono di scrivere manualmente metodi getter/setter, la necessità principale di Lombok è scomparsa.

  • RecyclerView DiffUtil

RecyclerView in Android è il widget. E ‘ in ogni applicazione in più schermi. Un componente importante quando si implementano gli adattatori RecyclerView è la classe DiffUtil. DiffUtil calcola il diff tra due elenchi per inviare le modifiche-se presenti-all’adattatore. È molto più efficiente di aggiornare l’intero elenco più e più volte e anima magnificamente i cambiamenti.

DiffUtil si basa molto sull’uguaglianza degli elementi, il che significa che due elementi sono uguali quando il loro contenuto è lo stesso. Ogni volta che usi un RecyclerView, dovresti usare DiffUtil, il che significa che hai bisogno di un modo per confrontare se due oggetti contengono le stesse informazioni. Questo è uno dei motivi per cui dobbiamo sovrascrivere equals nelle nostre classi di dati Java, ma con le classi Kotlin data viene fornito gratuitamente.

Nota: Se non si ha familiarità con DiffUtil controllare questa guida rapida.

  • Test

Nelle classi di test, controlliamo costantemente se i valori attesi corrispondono ai valori effettivi. Confrontare l’uguaglianza degli oggetti (come descritto prima) è un’attività molto comune.

Anche se non è necessario sovrascrivere la tripletta equals, toString e toHash per il normale runtime dell’app, le probabilità che sia necessario sovrascrivere tali metodi a scopo di test sono elevate, quindi le classi di dati Kotlin cancellano il percorso del test senza scuse.

Addendum

Parliamo di alcuni altri scenari comuni degni di nota quando si lavora con le classi di dati Kotlin. Questo non è strettamente correlato alle classi di dati, ma sono particolarmente comuni tra loro.

  • Costruttori multipli
data class User(val name : String, val age : Int)

Nella classe User che abbiamo definito in precedenza, dobbiamo specificare esplicitamente name e age quando si crea un’istanza come User("Steve", 56).

In Kotlin possiamo definire i valori predefiniti per gli argomenti in modo tale che nel caso in cui non passiamo un valore per quell’argomento, il valore predefinito viene assegnato ad esso.

data class User(val name : String, val age :Int = 0)

User("Steve", 56) è ancora valido, ma ora è consentito un secondo costruttore User("Steve"). In tal caso, il valore age sarà 0.

Possiamo assegnare valori predefiniti a entrambi i parametri consentendo un terzo costruttore User() dove name sarà vuoto e age sarà 0.

data class User(val name : String = "", val age : Int = 0)

Così ora User(), User("Steve") e User("Steve",56) sono tutte chiamate valide.

Questo ha alcune limitazioni: i parametri opzionali devono essere gli ultimi parametri nel costruttore. Il prossimo non verrà compilato.

data class User(val name : String = "", val age : Int)
val user = User(56) // This doesn't compile

Un’altra limitazione è che se abbiamo più parametri opzionali, devono essere saltati da destra a sinistra.

data class User(
val name : String,
val surname : String = "",
val age : Int = 0
)User("Steve")
User("Steve", "Jobs")
User("Steve", "Jobs", 56)
User("Steve",56) // This wont compile

Per gestire queste limitazioni Kotlin offre argomenti denominati. Questo ci permette di specificare a quale argomento appartiene ogni valore. Ora possiamo fare cose comeUser(name = "Steve", age = 56) — o più breve User("Steve", age = 56) – dove il cognome verrà assegnato al valore predefinito.

Gli argomenti predefiniti e gli argomenti denominati sono un modo molto utile per offrire più costruttori e sovraccarichi da una dichiarazione molto compatta.

  • @JvmOverloads

Tutto spiegato nel punto precedente non sta prendendo in considerazione le chiamate dal lato Java. Kotlin è interoperabile con Java, quindi dobbiamo essere in grado di creare istanze della classe User da Java.

Se non lo userai da Java, hai finito, ma per il resto, avrai bisogno dell’annotazione @JvmOverloads dato che Java non offre argomenti con nome.

data class User @JvmOverloads constructor(
val name : String,
val surname : String = "",
val age : Int = 0
)

Cosa sta facendo generando più costruttori in modo che possa essere chiamato da Java.

Nota: È importante notare che non creerà tutte le possibili permutazioni ma quelle risultanti dalla rimozione degli argomenti opzionali da destra a sinistra. Più qui

  • Costruttori

Dato Kotlin offre parametri denominati e argomenti predefiniti, rende meno probabile la necessità di costruttori. Inoltre, gli IDE moderni come Android Studio mostrano già il nome del parametro sul lato chiamante rendendo più facile la lettura rendendolo meno interessante solo per scopi di leggibilità.

Nei casi in cui il modello builder è ancora necessario. Kotlin non offre nulla di speciale per aiutarci con esso. Un modello di costruttore in Kotlin assomiglia a:

  • Annotazioni

È molto comune per alcune classi di modelli di dati utilizzare l’elaborazione delle annotazioni. Ad esempio, i modelli API (classi di dati per rappresentare risposte deserializzate dall’API) sono spesso annotati con annotazioni Gson o Moshi. I modelli di persistenza (classi di dati per rappresentare i dati memorizzati nella memoria locale/database) sono spesso annotati con annotazioni Room o Realm.

In Kotlin possiamo ancora usare quelle annotazioni. Ad esempio, questo è un modello utente da memorizzare nel database della stanza:

Sommario

Le classi di dati Kotlin sono il risultato di anni di apprendimento dal dolore e dalla frustrazione con le classi di dati in Java. Mirano ad avere tutti i vantaggi e nessuno degli aspetti negativi. Il loro utilizzo è molto semplice e divertente e una volta che ci si abitua ad esso, è molto difficile guardare indietro.

In Android, ci aiutano in modi diversi, ma soprattutto risparmiano molto tempo e riducono i bug.

Una classe di dati Kotlin è un buon esempio di ciò che Kotlin è come linguaggio di programmazione: conciso, pragmatico e una gioia per gli sviluppatori.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

lg