Une classe de données est un concept qui n’est lié à aucun langage de programmation spécifique, c’est un modèle assez pratique pour la plupart des programmeurs comme moyen simple de représenter, d’encapsuler et de déplacer des informations.

Une classe de données fait référence à une classe qui ne contient que des champs et des méthodes crud pour y accéder (getters et setters). Ce sont simplement des conteneurs pour les données utilisées par d’autres classes. Ces classes ne contiennent aucune fonctionnalité supplémentaire et ne peuvent pas fonctionner indépendamment sur les données qu’elles possèdent.

Habituellement, les classes de données représentent des entités du monde réel, et il est courant d’avoir des dizaines ou des centaines de ces classes dans un projet, ce qui signifie que créer, modifier et manipuler ces objets est une tâche très courante pour un développeur.

Une classe de données en Java

Voici à quoi ressemble habituellement une classe de données en Java:

Vous remarquerez que nous remplaçons les méthodes toString(), equals() et hashCode() (déclarées dans la classe Object.java). Pourquoi le remplacement de ces méthodes est-il pertinent dans les classes de données?

Lors de l’implémentation de ces trois méthodes, nous créons des objets de valeur, des classes pour lesquelles deux instances quelconques avec des valeurs de champ convenablement égales sont considérées comme interchangeables. (Remarque: Pour être complètement vrai, nous devrons rendre la classe immuable. Rendre les champs définitifs et supprimer les setters aide. L’immuabilité est souvent considérée comme une bonne pratique et recommandée lorsque cela est possible)

  • equals() : Par défaut, renvoie true uniquement si les deux variables font référence au même objet, c’est-à-dire si la position en mémoire est la même. Nous remplaçons cette méthode pour renvoyer true si les objets contiennent les mêmes informations, en d’autres termes, si les objets représentent la même entité. Il vérifie que les propriétés sont les mêmes et qu’elles contiennent la même valeur.
  • hashCode() : Par défaut, renvoie l’adresse mémoire de l’objet en hexadécimal. C’est une valeur numérique pour identifier un objet lors du test d’égalité, c’est-à-dire que les objets égaux ont le même code de hachage. Cette méthode doit être remplacée lorsque toString() est remplacée afin qu’elle renvoie un code de hachage calculé à partir des valeurs des propriétés.
  • toString(): Par défaut, renvoie le type d’objet et le hashCode(), par exemple [email protected]+. Nous remplaçons cette méthode pour avoir une version plus lisible de l’objet comme User(name=Steve, surname=Jobs).

Bien qu’il s’agisse d’un concept si important, rien dans le code Java ci-dessus ne rend cette classe différente des autres. Les programmeurs peuvent le reconnaître comme une classe de données en raison de la structure et des modèles de classe, mais du point de vue du compilateur, cette classe n’est qu’une autre classe.

La création de classes de données est si courante que les développeurs utilisent souvent l’E et d’autres plugins pour les aider dans cette tâche répétitive. La douleur de la création d’une classe de données en Java peut être atténuée par des plugins ou l’E, mais la plupart des bogues sont introduits lors de modifications ultérieures de ces classes. Il est très facile d’oublier de modifier toutes les méthodes associées en conséquence chaque fois qu’un champ est supprimé ou ajouté.

Une classe de données en Java ne prend pas en charge le langage. C’est une tâche répétitive et sujette aux bogues qui représente trop de friction pour un langage de programmation moderne.

Une classe de données dans Kotlin

La même classe de données dans Kotlin ressemblerait à ceci:

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

Kotlin élève les classes de données à des citoyens de première classe en introduisant le mot clé data. Décomposons-le.

  • Getters et setters

Les getters et setters sont créés automatiquement dans Kotlin lorsque nous déclarons des propriétés. En bref, ce que var name: String signifie, c’est que la classe User a une propriété publique (visibilité par défaut dans Kotlin), mutable (var) et est un type String. Étant donné que c’est public, il crée le getter, et étant donné que c’est mutable, il crée le setter.

Si nous voulons rendre la classe en lecture seule (pas de setters), nous devons utiliser val :

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

Nous pouvons mélanger val et var dans la même déclaration de classe. Vous pouvez considérer val comme une variable final en Java.

Tout ce qui a été expliqué jusqu’à présent, c’est commun à n’importe quelle déclaration de classe dans Kotlin, mais le mot clé data est ce qui fait la différence ici.

  • Le mot clé data

Déclarant une classe en tant que classe de données va nous permettre d’implémenter automatiquement toString(), hashCode() et equals() de la même manière que nous l’avons décrit ci-dessus pour la classe Java. Donc, si nous créons une classe utilisateur comme User("Steve Jobs",56) et appelons la méthode toString(), nous obtiendrons quelque chose comme: User(name=Steve Jobs, age=56).

Déclarations de déstructuration

Le mot clé data fournit des fonctions qui permettent les déclarations de déstructuration. En bref, cela crée une fonction pour chaque propriété afin que nous puissions faire des choses comme ça:

Fonction de copie

Le mot clé data nous donne un moyen pratique de copier des classes en modifiant la valeur de certaines propriétés. Si nous voulons créer une copie d’un utilisateur changeant d’âge, c’est ainsi que nous le ferions:

Les propriétés déclarées dans le corps de la classe sont ignorées

Le compilateur utilise uniquement les propriétés définies dans le constructeur principal pour les fonctions générées automatiquement.

La propriété address ne sera pas traitée par le mot clé data, cela signifie donc que les implémentations générées automatiquement l’ignoreront.

Pair et Triple

Pair et Triple sont des classes de données standard dans la bibliothèque, mais les documents Kotin eux-mêmes découragent leur utilisation en faveur de classes de données plus lisibles et personnalisées.

Exigences et limitations

  • Un constructeur de classe de données doit avoir au moins un paramètre.
  • Tous les paramètres doivent être commercialisés sous la forme val ou var.
  • Une classe de données ne peut pas être abstract, open, sealed ou inner.
  • equals , toString et les méthodes hashCode peuvent être explicitement remplacées.
  • Les implémentations explicites pour les fonctions componentN() et copy() ne sont pas autorisées.
  • Dériver une classe de données à partir d’un type avec une signature de correspondance de fonction copy() était obsolète dans Kotlin 1.2 et était interdit dans Kotlin 1.3.
  • Une classe data ne peut pas s’étendre d’une autre classe data.
  • Une classe data peut étendre d’autres classes (depuis Kotlin 1.1)

Les classes de données sont des citoyens de première classe à Kotlin. Dans une syntaxe très courte, ils offrent une solution sans friction avec tous les avantages et sans compromis.

Qu’est-ce que cela signifie pour Android

Je vais essayer d’expliquer quels sont les principaux avantages que j’ai trouvés en utilisant les classes de données Kotlin dans mes projets Android. Ce ne sont pas tous et ce ne sont peut-être pas les plus importants, mais ce sont les plus évidents de mon expérience jusqu’à présent.

  • Modèles de données

L’architecture dans Android était (et est toujours) un sujet brûlant. Il existe plusieurs options, mais la plupart d’entre elles ont en commun la séparation des préoccupations et des principes de responsabilité unique. L’architecture propre – très célèbre dans la communauté Android – est un ensemble de bonnes pratiques à suivre pour viser une bonne architecture qui met l’accent sur l’utilisation de différents modèles pour différentes couches de l’architecture. Cela signifie qu’un projet avec 3 ou 4 modèles de données différents devient standard.

Cela signifie que le nombre de classes de données dans un projet est très élevé et que des opérations telles que créer, lire, modifier, copier, comparer, cartographier are les classes de modèles de données sont des tâches quotidiennes qui bénéficient du code généré automatiquement par la classe de données Kotin. Permet d’économiser énormément de temps et de réduire les possibilités d’introduire des bugs.

  • Plus de valeur automatique

L’utilisation des types de valeur est une bonne pratique très étendue dans Android, en particulier parmi les classes de données.

Un type de valeur est un objet d’une classe de valeur immuable.
Une classe de valeur est une classe pour laquelle l’égalité dépend de son contenu.
Une classe immuable est une classe qui ne peut pas être modifiée après sa création.

AutoValue est une bibliothèque populaire de Google qui nous aide à créer des types de valeur. Il fait son travail mais il est très verbeux pour quelque chose qui ne devrait pas être si compliqué.

L’utilisation des classes kotlin data avec le modificateur d’accès val nous donne une approximation assez proche des types de valeurs.

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

La classe utilisateur précédente est une classe assez proche des types de valeur, dans une syntaxe beaucoup plus courte. Pour une bonne partie des gens, AutoValue peut être remplacé par Kotlin. Moins de code, pas de traitement des annotations et une bibliothèque de moins sur laquelle dépendre.

Remarque : Kotlin propose des propriétés et des classes en lecture seule avec le mot clé val. Lecture seule et immuable n’est pas la même chose (plus ici) mais en général, est considéré comme assez bon à des fins pratiques.

  • Plus de Lombok

L’une des façons dont les développeurs ont essayé de gagner du temps lorsqu’ils traitent avec des classes de données consiste à utiliser des bibliothèques pour générer des méthodes getter et setter. Lombok est l’un des (in) célèbres d’Android / Java. Il nécessite non seulement la bibliothèque mais aussi un plugin pour AS. La longue histoire courte est que pour la plupart des développeurs, Lombok apporte autant d’avantages que des maux de tête, donc il devient cette bibliothèque que vous commencez à aimer, mais après un certain temps, vous avez hâte de vous en débarrasser, mais vous ne le faites jamais car elle est partout.

Étant donné que les classes de données Kotlin ne nécessitent pas d’écrire manuellement des méthodes getter / setter, le besoin principal de Lombok a disparu.

  • RecyclerView DiffUtil

RecyclerView dans Android est le widget. C’est dans chaque application sur plusieurs écrans. Un composant important lors de l’implémentation des adaptateurs RecyclerView est la classe DiffUtil. DiffUtil calcule le diff entre deux listes afin d’envoyer les modifications – le cas échéant – à l’adaptateur. C’est beaucoup plus efficace que de rafraîchir toute la liste encore et encore, et cela anime magnifiquement les changements.

DiffUtil repose fortement sur l’égalité des éléments, ce qui signifie que deux éléments sont identiques lorsque leur contenu est le même. Chaque fois que vous utilisez un RecyclerView, vous devriez utiliser DiffUtil, ce qui signifie que vous avez besoin d’un moyen de comparer si deux objets contiennent les mêmes informations. C’est l’une des raisons pour lesquelles nous devons remplacer equals dans nos classes de données Java, mais avec les classes Kotlin data, cela est gratuit.

Remarque: Si vous n’êtes pas familiarisé avec DiffUtil, consultez ce guide de démarrage rapide.

  • Tests

Dans les classes de test, nous vérifions constamment si les valeurs attendues correspondent aux valeurs réelles. Comparer l’égalité des objets (comme décrit précédemment) est une tâche très courante.

Même si vous n’avez pas besoin de remplacer les triplets equals, toString et toHash pour votre exécution d’application régulière, les chances que vous deviez remplacer ces méthodes à des fins de test sont élevées, donc les classes de données Kotlin effacent le chemin des tests sans excuses.

Addendum

Parlons d’autres scénarios courants qui méritent d’être mentionnés lorsque vous travaillez avec des classes de données Kotlin. Ce n’est pas strictement lié aux classes de données, mais sont particulièrement courantes parmi elles.

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

Dans la classe User que nous avons définie précédemment, nous devons spécifier explicitement les name et age lors de la création d’une instance comme User("Steve", 56).

Dans Kotlin, nous pouvons définir des valeurs par défaut pour les arguments de telle sorte que si nous ne transmettons pas de valeur pour cet argument, la valeur par défaut lui est assignée.

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

User("Steve", 56) est toujours valide, mais maintenant un deuxième constructeur User("Steve") est autorisé. Dans ce cas, la valeur age sera 0.

Nous pouvons attribuer des valeurs par défaut aux deux paramètres permettant à un troisième constructeur User() où le name sera vide et le age sera 0.

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

Alors maintenant User(), User("Steve") et User("Steve",56) sont tous des appels valides.

Cela a quelques limitations: les paramètres optionnels doivent être les derniers paramètres du constructeur. Le suivant ne compilera pas.

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

Une autre limitation est que si nous avons plusieurs paramètres optionnels, ils doivent être ignorés de droite à gauche.

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

Pour faire face à ces limitations, Kotlin propose des arguments nommés. Cela nous permet de spécifier à quel argument appartient chaque valeur. Maintenant, nous pouvons faire des choses comme User(name = "Steve", age = 56) — ou plus court User("Steve", age = 56) – où le nom de famille sera attribué à la valeur par défaut.

Les arguments par défaut et les arguments nommés sont un moyen très pratique d’offrir plusieurs constructeurs et surcharges à partir d’une déclaration très compacte.

  • @ JvmOverloads

Tout ce qui est expliqué dans le point précédent ne prend pas en compte les appels du côté Java. Kotlin est interopérable avec Java, nous devons donc pouvoir créer des instances de la classe User à partir de Java.

Si vous ne l’utilisez pas à partir de Java, vous avez terminé, mais sinon, vous aurez besoin de l’annotation @JvmOverloads étant donné que Java n’offre pas d’arguments nommés.

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

Ce que cela fait en générant plusieurs constructeurs afin qu’il puisse être appelé à partir de Java.

Remarque: Il est important de noter que cela ne va pas créer toutes les permutations possibles mais celles résultant de la suppression des arguments optionnels de droite à gauche. Plus ici

  • Builders

Étant donné que Kotlin propose des paramètres nommés et des arguments par défaut, cela rend moins probable le besoin de builders. En plus de cela, lesEs modernes comme Android Studio affichent déjà le nom du paramètre du côté appelant, ce qui le rend plus facile à lire, ce qui le rend moins intéressant uniquement à des fins de lisibilité.

Dans les cas où le modèle de constructeur est toujours nécessaire. Kotlin n’offre rien de spécial pour nous y aider. Un modèle de constructeur dans Kotlin ressemble à:

  • Annotations

Il est très courant que certaines classes de modèles de données utilisent le traitement des annotations. Par exemple, les modèles d’API (classes de données pour représenter les réponses désérialisées de l’API) sont souvent annotés avec des annotations Gson ou Moshi. Les modèles de persistance (classes de données pour représenter les données stockées dans le stockage local / les bases de données) sont souvent annotés avec des annotations de pièce ou de domaine.

Dans Kotlin, nous pouvons toujours utiliser ces annotations. Par exemple, il s’agit d’un modèle utilisateur à stocker dans la base de données de la salle:

Résumé

Les classes de données Kotlin sont le résultat d’années d’apprentissage de la douleur et de la frustration avec les classes de données en Java. Ils visent à avoir tous les avantages et aucun des inconvénients. Leur utilisation est très simple et agréable et une fois que vous vous y êtes habitué, il est très difficile de regarder en arrière.

Dans Android, ils nous aident de différentes manières, mais surtout ils économisent beaucoup de temps et réduisent les bugs.

Une classe de données Kotlin est un bon exemple de ce qu’est Kotlin en tant que langage de programmation: concis, pragmatique et une joie pour les développeurs.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

lg