📚 Fonctionnalités du langage Kotlin
Kotlin est un langage qui support les paradigmes orienté objet et fonctionnel. Ce chapitre couvre les caractéristiques basiques et intermédiaires. Le chapitre suivant couvrira les fonctionnalités avancées.
Caractéristiques de base
Constructions de base (variables, flux de contrôle)
- Kotlin est typé statiquement et prend en charge le typage implicite.
- Typage statique : les types ne peuvent pas changer à l'exécution (c'est l'inverse du typage dynamique).
- Typage implicite : le compilateur peut inférer le type tant que c'est possible.
var
crée des variables ré-assignables.val
crée des variables qu'on ne peut plus ré-assigner.- Les points-virgules sont facultatifs.
- Kotlin prend en charge la déclaration de haut niveau des variables et des fonctions (elles peuvent être déclarées en dehors des classes).
- L'interpolation de chaîne de caractères est disponible avec cette syntaxe
${expression}
. if
etwhen
sont des expressions (elles peuvent renvoyer une valeur).when
est équivalent à l'instructionswitch
des autres langages- L'opérateur ternaire n'est pas disponible. L'expression
if
le remplace.
- for-each est le seul type de boucle for disponible.
- La programmation orientée objet est prise en charge comme en Java avec quelques fonctionnalités supplémentaires telles que les extensions.
- Le compilateur prend en charge la Null Safety. Il permet d'écrire du code sans erreur de pointeur nulle vérifié à la compilation.
- La programmation fonctionnelle est prise en charge (fonctions d'ordre supérieur et fonctions en tant qu'éléments de 1ère classe, etc.).
Utiliser val par défaut
Utiliser var
uniquement si vous réassignez une variable ou argument.
▶️ this code highlights the above features.
Les fonctions
Dans cette section, les termes 'argument' et 'paramètre' sont utilisés de manière interchangeable.
Les fonctions de Kotlin ont les caractéristiques suivantes :
- Déclaration :
fun functionName(arg1 : type1 = defaultvalue1, ...) : retunrType
. - Appeler une fonction en passant la valeur dans l'ordre de déclaration.
- Utilisez des étiquettes d'argument pour plus de clarté, cependant, cela permet également un classement arbitraire des arguments.
- Les arguments optionnels ont une valeur par défaut et peuvent être omis lors de l'appel.
- Les fonctions sont des éléments de première classe ou citoyens : elles peuvent être affectées à une variable, passées en tant que paramètre de fonction ou renvoyées par une fonction.
- 💡 Une fonction qui prend une fonction comme argument ou en renvoie une est une fonction d'ordre supérieur.
- Un type de fonction peut être exprimé comme suit :
(typeOfParam1, typeOfParam2, etc) -> returnType
(Le type de retour vide estUnit
). - Les fonctions anonymes utilisent la syntaxe suivante
{ argName1, argName2, etc. -> // code }
- Aussi appelées fonctions lambda ou fonctions littérales
- Le dernier argument de la fonction peut être mis après la fermeture après la parenthèse fermante
compute(9, 5) { x, y -> x * y }
▶️ Ce code illustre les fonctions en Kotlin.
La prochaine section abordera le null safety.
Null safety
null safety est une fonctionnalité du compilateur qui élimine la fameuse Null pointer exception ou npe. En effet, le compilateur signale des erreurs et des avertissements lorsque nous manipulons des types nullables (également appelées types optionnels) dès qu'il y a un risque de npe à l'exécution. Ainsi, afin de mettre Voici une liste des fonctionnalités de sécurité null fournies par Kotlin :
- Tous les types ne sont pas nullables par défaut ; nous ne pouvons pas affecter
null
à une variable ou à un argument.- Par exemple, ce code échoue
var s: String = null
.
- Par exemple, ce code échoue
- Un type peut être rendu nullable en le suffixant avec un ?. Par exemple :
var s : chaîne ? = nul
. - Kotlin interdit d'appeler une méthode ou une propriété de type non nullable, sauf si l'on fait l'une de ces possibilités :
- Utilisez le chaînage optionnel avec le suffixe ?.
- Fournissez une valeur par défaut avec l'opérateur elvis ?:.
- Smart-cast le nullable dans un non-nullable.
- Utilisez l'opérateur !! qui élimine les vérifications du compilateur. Cela ne devrait jamais être utilisé.
Ne jamais déballer avec !!
Car cela équivaut à désactiver la null safety. Utilisez les autres possibilités à la place.
▶️ ce code illustrate la null safety et les types optionnels.
La classe `Optional` de Java ne fournit aucun protection à la compilation
Ce code lance une npe en Java: Optional<String> s = null; s.isPresent();
. Le compilateur Java (au moins à la version version 17) ne propose pas d'équivalent à ce que propose Kotlin comme le smart casting.
Énumérations
Les énumérations permettent de travailler avec un groupe de valeurs de façon cadrée. Contrairement aux énumérations Java, les énumérations Kotlin sont des classes. Les enum class
de Kotlin fournissent ces fonctionnalités :
- Les expressions when prennent en charge les énumérations.
- Une
enum class
peut définir des méthodes et implémenter des interfaces mais elle ne peut pas dériver d'une classe. - Il existe des méthodes pour lister les constantes d'une
enum class
. - Chaque constante d'une énumération a des propriétés pour obtenir son nom et sa position (en commençant par 0).
▶️ ce code illustres les enum en Kotlin de façon succincte. Veuillez consulter la documentation officielle pour aller plus loin.
Exercices
Exercice 1
Veuillez cliquer sur le lien pour consulter l'énoncé
Déplier pour consulter la solution
Exercise 2
Veuillez cliquer sur le lien pour consulter l'énoncé
Déplier pour consulter la solution
Fonctionnalités intermédiaires
Programmation orientée objet
Kotlin permet d'écrire du code Orienté Object concis grâce aux caractéristiques suivantes :
- Concepts disponibles : classes, héritage, interfaces et classes abstraites.
- Prise en charge possée des propriétés : les getters et les setters sont automatiquement implémentés.
- On peut les personnaliser les accesseurs en définissant les fonctions
get()
etset(value)
à côté de la déclaration de la propriété.
- On peut les personnaliser les accesseurs en définissant les fonctions
- Les arguments du constructeur sont définis à côté du nom de la classe
class ClassName(arg1, atg2, )
- Préfixer les arguments d'un constructeur avec
val
ouvar
en fait une propriété (val
la rend non ré-assignable). - Le nom du constructeur est
init
et ne nécessite pas de paramètres. - Le compilateur vérifie que toutes les propriétés non nullables sont initialisées à la fin du constructeur.
- ⚠️ Le compilateur ne vérifie pas l'initialisation des propriétés
lateinit
. Ainsi, y accéder avant alors qu'elles ne sont pas initialisés provoque une exception.
- ⚠️ Le compilateur ne vérifie pas l'initialisation des propriétés
- Une classe doit être préfixée avec
open
pour permettre l'héritage. - Kotlin utilise le niveau d'accès
public
par défaut. - L'opérateur d'égalité
==
appelle implicitement la méthodeequals()
(contrairement à Java qui utilise l'égalité de référence). - Un objet compagnon contient des méthodes et des propriétés statiques.
- Les extensions ajoutent des fonctions et des propriétés aux classes existantes.
- 💡 Ils remplacent l'héritage dans de nombreuses situations.
- Par exemple, nous pouvons ajouter des fonctions à la classe String au lieu de créer une nouvelle classe
StringUtils
.
- Les classes et interfaces scellées ne peuvent pas être étendues ou implémentées par des tiers.
Ne définir les accesseurs que si vous avez un comportement personnalisé
Kotlin prend en charge les propriétés de façon plus poussée que Java et permet d'ajouter des accesseurs ultérieurement sans refactoriser le code qui appelle ces propriétés. Ainsi, par défaut, il suffit de définir le nom des propriétés sans accesseurs et on peut les utiliser directement.
▶️ ce code illustre la POO en Kotlin.
Data class
Ce sont des classes qui implémentent des méthodes communes:
equals()
,hashCode()
,copy()
ettoString()
componentN()
qui est une syntaxe alternative pour récupérer les propriétés.
Cependant, les data class ont des restrictions:
- Le constructeur principal doit avoir au moins un paramètre.
- Tous les paramètres du constructeur principal doivent être marqués comme
val
ouvar
. - Une Data class ne peut pas être abstraite, ouverte à l'héritage, scellée ou interne (💡 mais des extensions sont possibles).
▶️ ce code illustrate les data class.
Programmation fonctionnelle
Concepts généraux
La programmation fonctionnelle s'articule autour de ces concepts : fonctions pures, récursivité, transparence référentielle, variables immuables, fonctions en tant que citoyens de première classe et fonctions d'ordre supérieur.
Expliquons brièvement ces concepts :
- Les variables immuables signifient qu'on ne peut pas changer la valeur d'une variable ou ses propriétés une fois qu'elle a été créée. Si nous voulons le faire, nous devons créer une nouvelle instance avec la nouvelle valeur.
- Les fonctions pures sont des fonctions qui n'ont pas d'effets secondaires et renverront donc toujours la même sortie étant donné la même entrée.
- Les fonctions sont des citoyennes de première classe : elles peuvent être affectées à une variable ou utilisées dans des fonctions d'ordre supérieur (passées en tant qu'un argument de fonction ou retournées dans un fonction).
- Transparence référentielle : signifie qu'une expression peut être remplacée par son résultat sans modifier le comportement du programme.
💡 Les langages fonctionnels purs fournissent ces fonctionnalités de manière native et les appliquent (au moment de la construction).
Kotlin et programmation fonctionnelle
Kotlin n'est pas un langage fonctionnel pur mais il prend en charge certaines fonctionnalités. En effet, Kotlin ne sait pas dire si une fonction est pures ou non, mais il fournit des collections immuables via la bibliothèque kotlinx.collections.immutable pour nous aider à manipuler des données immuables.
`listOf` génère des listes en lecture seule, mais qui sont mutables
Une liste en lecture seule ne peut pas ajouter ou supprimer des éléments, mais elle peut modifier les données sous-jacentes.
@Test
fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
val list: List<String> = listOf("This", "Is", "Totally", "Immutable")
(list as MutableList<String>)[2] = "Not"
assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
}
La librairie Arrow-kt permet d'aller encore plus loin en développement fonctionnel.
Programmation déclarative
La programmation déclarative est un style célèbre dans la programmation fonctionnelle. Il consiste à écrire du code sous la forme d'un enchaînement d'appels de fonction dans ce style val result = f(x).g(y). ...
. Les fonctions d'ordre supérieur remplacent de nombreuses situations où nous utiliserions des boucles. Cela favorise le code lisible qui est facile à déboguer et à maintenir.
▶️ ce code montre comment manipuler une liste avec la programmation déclarative.
Kotlin and Java interoperability
- Kotlin is designed with Java interoperability in mind.
- Kotlin code may require some annotations to be called from Java.
- It is possible to mix Java and Kotlin in the same project.
- JetBrain's IntelliJ and Android Studio can convert to Kotlin when pasting java code.
- Kotlin generates Java records by annotating a data class with
@JvmRecord
and targeting JVM 16, among other requirement listed here. - It is much more easier and natural to call Java from Kotlin.
- For example: Java accessors are converted to Kotlin properties.
▶️ this code shows how to convert a Kotlin List
to a Java ArrayList
.
The official documentation provides exhaustive documentation on Kotlin and JVM integration