Kotlin From Scratch Propriétés et classes avancées

Kotlin est un langage de programmation moderne qui compile en bytecode Java. C'est gratuit et open source, et promet de rendre le codage pour Android encore plus amusant.  

Dans l'article précédent, vous avez appris sur les classes et les objets dans Kotlin. Dans ce didacticiel, nous allons continuer à en apprendre davantage sur les propriétés et également à examiner les types avancés de classes dans Kotlin en explorant les éléments suivants:

  • propriétés initialisées tardivement
  • propriétés en ligne 
  • propriétés d'extension
  • classes data, enum, imbriquées et scellées

1. Propriétés initialisées tardives

Nous pouvons déclarer une propriété non nulle dans Kotlin comme initialisé tardivement. Cela signifie qu'une propriété non NULL ne sera pas initialisée au moment de la déclaration avec une initialisation valeur réelle qui ne se produira pas via un constructeur, mais qu'elle sera initialisée tardivement par une méthode ou une injection de dépendance..

Regardons un exemple pour comprendre ce modificateur de propriété unique. 

class Presenter dépôt privé var: référentiel? = null fun initRepository (référentiel: référentiel): Unit this.repository = repo class Repository fun saveAmount (amount: Double)  

Dans le code ci-dessus, nous avons déclaré un mutable nullable dépôt propriété qui est de type Dépôt-à l'intérieur de la classe Présentateur-et nous avons ensuite initialisé cette propriété à null pendant la déclaration. Nous avons une méthode initRepository () dans le Présentateur classe qui réinitialise cette propriété plus tard avec un réel Dépôt exemple. Notez que vous pouvez également affecter une valeur à cette propriété à l'aide d'un injecteur de dépendance tel que Dagger..     

Maintenant, invoquons des méthodes ou des propriétés sur cette dépôt propriété, nous devons faire une vérification nulle ou utiliser l'opérateur d'appel sécurisé. Pourquoi? Parce que le dépôt propriété est de type nullable (Dépôt?). (Si vous avez besoin d'un rappel sur l'annulation de la nullité dans Kotlin, veuillez visiter Nullability, Loops et Conditions).

// Dans la classe Presenter fun save (montant: double) référentiel? .SaveAmount (montant)

Pour éviter de devoir effectuer des vérifications nulles chaque fois que nous devons appeler la méthode d'une propriété, nous pouvons marquer cette propriété avec le symbole en retard modificateur-cela signifie que nous avons déclaré cette propriété (qui est une instance d'une autre classe) comme initialisé tardivement (signifiant que la propriété sera initialisée plus tard).  

class Presenter dépôt tardif privé private late: Repository //…

Désormais, tant que nous attendons qu'une valeur soit attribuée à la propriété, nous pouvons accéder aux méthodes de la propriété sans effectuer de contrôle null. L'initialisation de la propriété peut se produire soit dans une méthode setter, soit par injection de dépendance. 

repository.saveAmount (montant)

Notez que si nous essayons d’accéder aux méthodes de la propriété avant son initialisation, nous obtiendrons un kotlin.UninitializedPropertyAccessException au lieu d'une NullPointerException. Dans ce cas, le message d'exception sera "le référentiel de propriété Lateinit n'a pas été initialisé". 

Notez également les restrictions suivantes lors du retard d’initialisation d’une propriété avec en retard:

  • Il doit être mutable (déclaré avec var).
  • Le type de propriété ne peut pas être un type primitif, par exemple, Int, Double, Flotte, etc. 
  • La propriété ne peut pas avoir un getter ou un setter personnalisé.

2. Propriétés en ligne

Dans Advanced Functions, j’ai introduit le en ligne modificateur pour les fonctions d'ordre supérieur - ceci permet d'optimiser les fonctions d'ordre supérieur qui acceptent un lambda en tant que paramètre. 

À Kotlin, nous pouvons également utiliser cette en ligne modificateur sur les propriétés. L'utilisation de ce modificateur optimisera l'accès à la propriété.

Voyons un exemple pratique. 

classe Student val nickName: String get () println ("Nom de Nick récupéré") retourne "koloCoder" fun main (args: Array) val student = Student () print (student.nickName)

Dans le code ci-dessus, nous avons une propriété normale, surnom, ça n'a pas le en ligne modificateur. Si nous décompilons l’extrait de code, utilisez la commande Afficher le bytecode de Kotlin fonction (si vous utilisez IntelliJ IDEA ou Android Studio, utilisez Outils > Kotlin > Afficher le bytecode de Kotlin), nous verrons le code Java suivant:

public final class Student @NotNull public final String getNickName () String var1 = "Pseudo récupéré"; System.out.println (var1); retourne "koloCoder";  public final class InlineFunctionKt public final final statique principal (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Étudiant étudiant = nouvel étudiant (); String var2 = student.getNickName (); System.out.print (var2); 

Dans le code Java généré ci-dessus (certains éléments du code généré ont été supprimés pour des raisons de brièveté), vous pouvez voir que principale() méthode le compilateur a créé un Étudiant objet, appelé le getNickName () méthode, puis imprimé sa valeur de retour.  

Spécifions maintenant la propriété comme en ligne à la place, et comparez le bytecode généré.

//… inline val nickName: String //… 

Nous venons d'insérer le en ligne modificateur avant le modificateur de variable: var ou val. Voici le bytecode généré pour cette propriété inline:

//… public final final statique principal (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Étudiant étudiant = nouvel étudiant (); String var3 = "Pseudo récupéré"; System.out.println (var3); String var2 = "koloCoder"; System.out.print (var2);  //… 

Encore une fois, du code a été supprimé, mais l’essentiel à noter est le principale() méthode. Le compilateur a copié la propriété obtenir() corps de la fonction et collé dans le site d'appel (ce mécanisme est similaire aux fonctions inline). 

Notre code a été optimisé car il n'est pas nécessaire de créer un objet et d'appeler la méthode property getter. Mais, comme discuté dans les fonctions inline post, nous aurions un plus gros bytecode qu’avant, alors utilisez-le avec prudence. 

Notez également que ce mécanisme fonctionnera pour les propriétés qui n'ont pas de champ de sauvegarde (rappelez-vous qu'un champ de sauvegarde est simplement un champ utilisé par les propriétés lorsque vous souhaitez modifier ou utiliser ces données de champ).. 

3. Propriétés de l'extension 

Dans Advanced Functions, j'ai également abordé les fonctions d'extension. Celles-ci nous permettent d'étendre une classe avec de nouvelles fonctionnalités sans avoir à hériter de cette classe. Kotlin fournit également un mécanisme similaire pour les propriétés, appelé propriétés d'extension

val String.upperCaseFirstLetter: String get () = this.substring (0, 1) .toUpperCase (). plus (this.substring (1))

Dans le post Fonctions avancées, nous avons défini un uppercaseFirstLetter () fonction d'extension avec type de récepteur Chaîne. Ici, nous l'avons converti en une propriété d'extension de premier niveau. Notez que vous devez définir une méthode de lecture sur votre propriété pour que cela fonctionne.. 

Donc, avec ces nouvelles connaissances sur les propriétés d'extension, vous saurez que si vous avez toujours souhaité qu'une classe ait une propriété qui n'était pas disponible, vous êtes libre de créer une propriété d'extension de cette classe.. 

4. Classes de données

Commençons par une classe Java typique ou POJO (Plain Old Java Object). 

classe publique BlogPost private final String title; URL finale de l'URI privée; description finale privée de la chaîne; private final Date publishDate; //… constructeur non inclus par souci de brièveté @Override public boolean égal à (Object o) if (this == o) return true; if (o == null || getClass ()! = o.getClass ()) renvoie false; BlogPost blogPost = (BlogPost) o; if (title! = null?! title.equals (blogPost.title): blogPost.title! = null) return false; if (url! = null?! url.equals (blogPost.url): blogPost.url! = null) return false; if (description! = null?! description.equals (blogPost.description): blogPost.description! = null) return false; return publishDate! = null? publishDate.equals (blogPost.publishDate): blogPost.publishDate == null;  @Override public int hashCode () int result = title! = Null? title.hashCode (): 0; result = 31 * result + (url! = null? url.hashCode (): 0); result = 31 * result + (description! = null? description.hashCode (): 0); result = 31 * result + (publishDate! = null? publishDate.hashCode (): 0); retourne le résultat;  @Override public String toString () return "BlogPost " + "title = '" + title +' \ "+", url = "+ url +", + description + "\" + ", publishDate =" + publishDate + '';  //… les setters et getters ont également été ignorés pour des raisons de brièveté

Comme vous pouvez le constater, nous devons coder explicitement les accesseurs de propriété de classe: le getter et le setter, ainsi que hashcodeéquivaut à, et toString méthodes (même si IntelliJ IDEA, Android Studio ou la bibliothèque AutoValue peuvent nous aider à les générer). Nous voyons ce type de code passe-partout dans la couche de données d'un projet Java typique. (J'ai enlevé les accesseurs de champ et le constructeur par souci de brièveté). 

Ce qui est bien, c’est que l’équipe Kotlin nous a fourni le Les données modificateur pour les classes éliminant l'écriture.

Écrivons maintenant le code précédent dans Kotlin à la place.

classe de données BlogPost (titre var: String, URL var: URI, description var: String, var publishDate: Date)

Impressionnant! Nous spécifions simplement le Les données modificateur avant le classe mot-clé pour créer une classe de données, comme ce que nous avons fait dans notre BlogPost Kotlin classe ci-dessus. Maintenant le équivaut àhashcodetoStringcopie, et des méthodes à composants multiples seront créées sous le capot pour nous. Notez qu'une classe de données peut étendre d'autres classes (ceci est une nouvelle fonctionnalité de Kotlin 1.1). 

le équivaut à Méthode

Cette méthode compare l'égalité des objets entre deux objets et renvoie true s'ils sont égaux ou false sinon. En d'autres termes, il compare si les deux instances de la classe contiennent les mêmes données. 

student.equals (student3) // utilisation du == dans Kotlin student == student3 // identique à l’utilisation de equals ()

En Kotlin, en utilisant l'opérateur d'égalité == appellera le équivaut à méthode dans les coulisses.

le hashCode Méthode 

Cette méthode retourne une valeur entière utilisée pour le stockage et la récupération rapides des données stockées dans une structure de données de collecte basée sur un hachage, par exemple dans le répertoire. HashMap et HashSet types de collection.  

le toString Méthode

Cette méthode retourne un Chaîne représentation d'un objet. 

Classe de données Person (var firstName: String, var lastName: String) val person = Person ("Chike", "Mgbemena") println (personne) // affiche "Person (firstName = Chike, lastName = Mgbemena)"

En appelant simplement l'instance de la classe, nous obtenons un objet de chaîne renvoyé à nous. Kotlin appelle l'objet. toString () sous le capot pour nous. Mais si on ne met pas le Les données mot-clé in, voyez ce que notre représentation de chaîne d'objet serait: 

com.chike.kotlin.classes.Person@2f0e140b

Beaucoup moins informatif!

le copie Méthode

Cette méthode nous permet de créer une nouvelle instance d'un objet avec toutes les mêmes valeurs de propriété. En d'autres termes, il crée une copie de l'objet. 

val person1 = Person ("Chike", "Mgbemena") println (person1) // Person (prénom = Chike, lastName = Mgbemena) val person2 = person1.copy () println (person2) // personne (prénom = Chike, lastName = Mgbemena)

Une bonne chose à propos du copie méthode en Kotlin est la possibilité de changer les propriétés pendant la copie. 

val person3 = person1.copy (lastName = "Onu") println (person3) // Person3 (firstName = Chike, lastName = Onu)

Si vous êtes un codeur Java, cette méthode est similaire à la cloner() méthode que vous connaissez déjà. Mais le Kotlin copie méthode a des fonctionnalités plus puissantes. 

Déclaration destructive

dans le La personne classe, nous avons également deux méthodes générées automatiquement pour nous par le compilateur en raison de la Les données mot clé placé dans la classe. Ces deux méthodes sont préfixées par "composant", puis suivies d'un suffixe numérique: composant1 ()composant2 (). Chacune de ces méthodes représente les propriétés individuelles du type. Notez que le suffixe correspond à l'ordre des propriétés déclarées dans le constructeur principal.

Donc, dans notre exemple d'appel composant1 () retournera le prénom, et en appelant composant2 () renverra le nom de famille.

println (person3.component1 ()) // Chike println (person3.component2 ()) // Onu

L'appel des propriétés à l'aide de ce style est difficile à comprendre et à lire. Il est donc préférable d'appeler explicitement le nom de la propriété. Cependant, ces propriétés créées implicitement ont un objectif très utile: elles nous permettent de faire une déclaration de déstructuration dans laquelle nous pouvons affecter chaque composant à une variable locale..

val (prenom, prenom) = personne ("Angelina", "Jolie") println (prenom + "" + nom) // Angelina Jolie

Ce que nous avons fait ici est d’affecter directement les première et deuxième propriétés (Prénom et nom de famille) du La personne taper aux variables Prénom et nom de famille respectivement. J'ai aussi discuté de ce mécanisme appelé déclaration de déstructuration dans la dernière section de la publication Packages and Basic Functions. 

5. Classes imbriquées

Dans l'article Plus de plaisir avec les fonctions, je vous ai dit que Kotlin prend en charge les fonctions locales ou imbriquées, une fonction déclarée dans une autre fonction. De même, Kotlin prend également en charge les classes imbriquées, une classe créée dans une autre classe.. 

classe OuterClass classe NestedClass fun nestedClassFunc () 

Nous appelons même les fonctions publiques de la classe imbriquée, comme on le voit ci-dessous: une classe imbriquée dans Kotlin équivaut à statique classe imbriquée en Java. Notez que les classes imbriquées ne peuvent pas stocker une référence à leur classe externe. 

val nestedClass = OuterClass.NestedClass () nestedClass.nestedClassFunc ()

Nous sommes également libres de définir la classe imbriquée comme privée. Cela signifie que nous ne pouvons créer qu'une instance du NestedClass dans le cadre de la OuterClass

Classe intérieure

Les classes internes, en revanche, peuvent référencer la classe externe dans laquelle elle a été déclarée. Pour créer une classe interne, nous plaçons le interne mot clé avant le classe mot clé dans une classe imbriquée. 

class OuterClass () val oCPropt: String = "Yo" classe intérieure InnerClass fun innerClassFunc () val outerClass = this @ OuterClass print (outerClass.oCPropt) // affiche "Yo"

Ici nous référons le OuterClass du InnerClass en utilisant  ceci @ OuterClass.

6. Cours d'énumération

Un type enum déclare un ensemble de constantes représentées par des identificateurs. Ce type spécial de classe est créé par le mot clé enum qui est spécifié avant la classe mot-clé. 

enum class Country NIGERIA, GHANA, CANADA

Pour récupérer une valeur enum basée sur son nom (comme en Java), procédez comme suit:

Country.valueOf ("NIGERIA")

Ou nous pouvons utiliser le Kotlin enumValueOf() méthode d'assistance pour accéder aux constantes de manière générique:

enumValueOf("NIGERIA")

De plus, nous pouvons obtenir toutes les valeurs (comme pour une énumération Java) comme ceci:

Pays.valeurs ()

Enfin, nous pouvons utiliser le Kotlin enumValues() méthode d'assistance pour obtenir toutes les entrées enum de manière générique:

enumValues()

Ceci retourne un tableau contenant les entrées enum.  

Enum Constructeurs

Tout comme une classe normale, le enum type peut avoir son propre constructeur avec des propriétés associées à chaque constante enum. 

enum class Country (code appelant val: Int) NIGERIA (234), USA (1), GHANA (233)

dans le Pays constructeur primaire de type enum, nous avons défini la propriété immuable appelantCodes pour chaque constante enum. Dans chacune des constantes, nous avons passé un argument au constructeur. 

Nous pouvons alors accéder à la propriété constants comme ceci:

val pays = pays.NIGERIA print (country.callingCode) // 234

7. Classes scellées

Une classe scellée dans Kotlin est une classe abstraite (vous ne souhaitez jamais en créer d'objets) que d'autres classes peuvent étendre. Ces sous-classes sont définies dans le corps de la classe scellée du même fichier. Toutes ces sous-classes étant définies à l'intérieur du corps de classe scellé, nous pouvons connaître toutes les sous-classes possibles en visualisant simplement le fichier.. 

Voyons un exemple pratique. 

// shape.kt classe scellée Classe de forme Cercle: Classe de forme () Triangle: Classe de forme () Rectangle: Forme ()

Pour déclarer une classe comme scellée, nous insérons le scellé modificateur avant le classe modificateur dans la déclaration de classe en-tête-dans notre cas, nous avons déclaré le Forme classe comme scellé. Une classe scellée est incomplète sans ses sous-classes, tout comme une classe abstraite typique. Nous devons donc déclarer les sous-classes individuelles dans le même fichier (forme.kt dans ce cas). Notez que vous ne pouvez pas définir une sous-classe d'une classe scellée à partir d'un autre fichier.. 

Dans notre code ci-dessus, nous avons spécifié que le Forme la classe ne peut être prolongée que par les classes CercleTriangle, et Rectangle.

Les classes scellées à Kotlin ont les règles supplémentaires suivantes:

  • On peut ajouter le modificateur abstrait à une classe scellée, mais cela est redondant car les classes scellées sont abstraites par défaut.
  • Les classes scellées ne peuvent pas avoir le ouvrir ou final modificateur. 
  • Nous sommes également libres de déclarer des classes de données et des objets en tant que sous-classes d'une classe scellée (ils doivent encore être déclarés dans le même fichier).. 
  • Les classes scellées ne sont pas autorisées à avoir des constructeurs publics - leurs constructeurs sont privés par défaut. 

Les classes qui étendent des sous-classes d'une classe scellée peuvent être placées dans le même fichier ou dans un autre fichier. La sous-classe de classe scellée doit être marquée avec le ouvrir modificateur (vous en apprendrez plus sur l'héritage à Kotlin dans le prochain article). 

// employee.kt sealed classe Employee open class Artiste: Employee () // musician.kt class Musicien: Artiste ()

Une classe scellée et ses sous-classes sont vraiment pratiques dans un quand expression. Par exemple:

fun whatIsIt (shape: Shape) = quand (shape) est Circle -> println ("Un cercle") est Triangle -> println ("Un triangle") est Rectangle -> println ("Un rectangle")

Ici, le compilateur est intelligent pour assurer que nous avons couvert tous les possibles quand cas. Cela signifie qu'il n'est pas nécessaire d'ajouter le autre clause. 

Si nous devions plutôt procéder comme suit:

fun whatIsIt (shape: Shape) = quand (shape) est Circle -> println ("Un cercle") est Triangle -> println ("Un triangle")

Le code ne compilerait pas car nous n'avons pas inclus tous les cas possibles. Nous aurions l'erreur suivante:

Kotlin: à la place, l'expression 'quand' doit être exhaustive, ajouter plutôt que 'est une branche' rectangle 'ou une branche' else '.

Donc, nous pourrions soit inclure le est rectangle cas ou inclure le autre clause pour compléter le quand expression. 

Conclusion

Dans ce tutoriel, vous en avez appris plus sur les cours de Kotlin. Nous avons couvert les éléments suivants concernant les propriétés de classe:

  • initialisation tardive
  • propriétés en ligne 
  • propriétés d'extension

En outre, vous avez découvert certaines classes intéressantes et avancées telles que les classes data, enum, imbriquées et scellées. Dans le prochain didacticiel de la série Kotlin From Scratch, vous découvrirez les interfaces et l'héritage dans Kotlin. À bientôt!

Pour en savoir plus sur la langue Kotlin, je vous recommande de consulter la documentation Kotlin. Ou consultez certains de nos autres articles sur le développement d'applications Android ici sur Envato Tuts!